geo Query replicating from ruby to Ecto Elixir - ruby

In Ruby I have this function query.by_distance where as by_distance is something like
def_dataset_method(:by_distance) do |from, meters|
point = Geocoding.as_point(from)
query = "ST_DWithin(location, ST_SetSRID(ST_Point(?, ?), 4326)::geography, ?)"
where(query, point.lng, point.lat, meters)
end
in a ruby endpoint user is passing 2 values which are is_near_to which is mostly name of a city or a country.. Through which Geecoding is getting its points and 2 value is within_distance which are meters for getting cameras within this distance.
The above is happening in Ruby.
What am doing in Elixir to replicate it is
def by_distance(query, is_near_to, within_distance) do
[%{"lat" => lat, "lng" => lng}] = fetch(is_near_to)
latitude = lat
longitude = lng
query
|> where([cam], st_dwithin(cam.location, st_set_srid(st_point_from_text(^"#{latitude},#{longitude}"), 4326), ^within_distance))
end
for getting lat and long am doing something as
defmodule EvercamMedia.Geocode do
def fetch(address) do
response = HTTPotion.get "http://maps.googleapis.com/maps/api/geocode/json?address=#{URI.encode(address)}&sensor=false"
{:ok, results} = Poison.decode response.body
get_in(results, ["results", Access.all(), "geometry", "location"])
end
end
now I have same scenarios. I have both values is_near_to and within_distance. But am totally unaware of this that how I can replicate the same thing in a query as we are using geo in our project and by the docs its clearly possible but am not getting the way how to do it.

This is part of an app I'm working on, I believe it does what you need, it uses a fragment to pass the ST_distance_sphere call directly to postgis.
Using the library https://github.com/bryanjos/geo and postgis
In the model
def near_by(query, point, distance) do
from place in query,
where: fragment("ST_distance_sphere(?,?)", place.location, ^point) >= ^distance,
order_by: fragment("ST_distance_sphere(?,?)", place.location, ^point),
select: {place, fragment("ST_distance_sphere(?,?)", place.location, ^point)}
end
In the controller
def near_by(conn, %{"latitude" => latitude, "longitude" => longitude, "distance" => distance}) do
point = %Geo.Point{coordinates: {String.to_float(latitude), String.to_float(longitude)}, srid: 4326}
places = Place
|> Place.near_by(point, String.to_float(distance))
|> Repo.all
render(conn, "near_by.json", places: places)
end
In the view
def render("near_by_place.json", %{place: place}) do
{ place, distance } = place
{lat, lon} = place.location.coordinates
%{id: place.id,
name: place.name,
latitude: lat,
longitude: lon,
distance: distance}
end

Related

rest_framework custom order_by

let's see if I can ask the questions correctly?
model.py
class Point(models.Model):
name = models.CharField(
"nome del sistema",
max_length=128,
)
x = models.FloatField(
"cordinata spaziale x",
)
y = models.FloatField(
"cordinata spaziale y",
)
z = models.FloatField(
"cordinata spaziale z",
)
distance = float(0)
def __str__(self) -> str:
return f"{self.name}"
#property
def get_distance(self):
return self.distance
#get_distance.setter
def get_distance(self, point):
"""
ritorna la distanza che ce tra due sistemi
"""
ad = float((point.x-self.x) if point.x > self.x else (self.x-point.x))
bc = float((point.y-self.y) if point.y > self.y else (self.y- point.y))
af = float((point.z-self.z) if point.z > self.z else (self.z-point.z))
return (pow(ad,2)+pow(bc,2)+pow(af,2))**(1/2)
class Meta:
verbose_name = "point"
verbose_name_plural = "points"
in the particular model there are two defs which calculate, save and return the distance with respect to the point we pass them
wenws.py
class PointViewset(viewsets.ModelViewSet):
"""
modelo generico per un systema
"""
queryset = Point.objects.all()
serializer_class = PointSerializers
filterset_class = PointFilter
in the wenws not that much particular to explain and base base the only thing we have to say and that as filters I use 'django_filters'
filters.py
import django_filters as filters
import Point
class CustomOrderFilter(filters.CharFilter):
def filter(self, qs:QuerySet, value):
if value in ([], (), {}, '', None):
return qs
try:
base = qs.get(name=value)
for point in qs:
point.get_distance = base
qs = sorted(qs, key= lambda x: x.get_distance)
except Point.DoesNotExist:
qs = qs.none()
return qs
class PointFilter(filters.rest_framework.FilterSet):
security = filters.ChoiceFilter(choices=security_choices
point= CustomCharFilter(
label = "point"
)
class Meta:
model = Point
fields = {
'name':['exact'],
}
now the complicated thing with 'CustomCharFilter' I pass in the http request the name of the system which then returns to me in the filter as value after I check that it is not empty and I start with returning the point that I have passed with base = qs.get ( name = value)
to then calculate and save the distance for each point with point.get_distance = base '' on the inside of the for, at the end I reorder the QuerySet with qs = sorted (qs, key = lambda x: x.get_distance) '' the problem that both with this way and with another that I have tried the QuerySet it 'transforms' into a list and this does not suit me since I have to return a QuerySet in the order of here I want. I don't know how to do otherwise, since order_by I can't use it since the distance is not inside the database
can someone help me?
So the problem is that you want to filter from a python function which cant be done in a query, as they only speak SQL.
The easy slow solution is to do the filtering in python, this might work if there are just a few Points.
points = list(Point.objects.all())
points.sort(cmp=comparison_function)
The actual real solution is to port that math to Djangos ORM and annotate your queryset with the distance to a given point, this is quite an advanced query, If you tell us your database server you use we can probably help you with that too.
ps. There is an abs() function in python to get absolute value instead of the if/else in get_distance

How to use scopes to filter data by partial variable

I have some working script filtering my results with Active Record Scoping. Everything works fine when i want to filter by comparing params with data from database.
But i have some function counting car price inside _car.html.erb partial, the result of this function depends on params result.
How can i scope search results by result of this function and show only cars which are under some price (defined in params).
Some code to make it more clear:
car.rb (model file)
scope :price_leasing, -> (price_leasing) { where('price_leasing <= ?', price_leasing) }
# for now price_leasing is getting price from database
scope :brand, -> (brand) { where brand: brand }
scope :car_model, -> (car_model) { where car_model: car_model }
scope :category, -> (category) { where category: category }
cars_controller.rb (controller file)
def index
#cars = Car.where(nil)
#cars = #cars.price_leasing(params[:price_leasing]) if params[:price_leasing].present?
#cars = #cars.brand(params[:brand]) if params[:brand].present?
#cars = #cars.car_model(params[:car_model]) if params[:car_model].present?
#cars = #cars.category(params[:category]) if params[:category].present?
#brands = Brand.all # importing all car brands into filters
end
in index.html.erb i have "render #cars" code
<%=
if #cars.size > 0
render #cars.where(:offer_status => 1)
else
render html: '<p>Nie znaleziono pasujących wyników.</p>'.html_safe
end
%>
inside _car.html.erb file i have function from helper
<h3 class="car-cell__price"><%= calculate_car_price(car.price, car.id) %> <span class="car-cell__light-text">zł/mc</span></h3>
my calculate_car_price() function inside helper
def calculate_car_price(car_price, car_id)
car = Car.find(car_id)
fullprice = car_price
if params[:price_leasing].present?
owncontribution = params[:price_leasing].to_i
else
owncontribution = car.owncontribution
end
pv = fullprice - owncontribution + (0.02 * fullprice)
if params[:period].present?
carperiod = params[:period].to_i
carprice = (Exonio.pmt(0.0522/60, carperiod, pv)) * -1
else
carprice = (Exonio.pmt(0.0522/60, 60, pv)) * -1
end
p number_with_precision(carprice, precision: 0)
end
i would love to scope by the result of this function. Is it possible?
The thing about scopes are that they are implemented at the database level. You want to select records that have a value
To do what you want at the DB level, you would need to a virtual column in the database, the implementation will change based on which database product you're using (I would not expect a virtual column definition in postgreSQL to be the same as a virtual column definition in mySQL)
So I'm not sure there's an optimal way to do this.
I would suggest you build your own class method and instance method in your model Car. It would be less performant but easier to understand and implement.
def self.car_price_under(target_price, params)
select { |v| v.car_price_under?(target_price, params) }
end
def car_price_under?(target_price, params)
full_price = price
if params[:price_leasing].present?
my_own_contribution = params[:price_leasing].to_i
else
my_own_contribution = owncontribution
end
pv = full_price - my_own_contribution + (0.02 * full_price)
if params[:period].present?
car_period = params[:period].to_i
new_car_price = (Exonio.pmt(0.0522/60, car_period, pv)) * -1
else
new_car_price = (Exonio.pmt(0.0522/60, 60, pv)) * -1
end
new_car_price <= target_price
end
This would let you do...
#cars = Car.where(nil)
#cars = #cars.brand(params[:brand]) if params[:brand].present?
#cars = #cars.car_model(params[:car_model]) if params[:car_model].present?
#cars = #cars.category(params[:category]) if params[:category].present?
#cars = #cars.car_price_under(target_price, params) if target_price.present?
Note that this is happening in rails, NOT in the database, so the car_price_under method should be called after all other scopes to minimise the number of records that need to be examined. You cannot chain additional scopes as #cars would be an array... if you want to be able to chain additional scopes (or you want an active record relation, not an array) you could do something like #cars = Car.where(id: #cars.pluck(:id))

Query REST API latitude and longitude

I want my users to query two slugs fields (latitude, longitude) and then the 2 slug fields get compared to find nearest distance within 1.5km radius and display the api according to the nearest safehouses.
For example: when the users add latitude, longitude in their query,
www.example.com/safeplace/?find=-37.8770,145.0442
This will show the nearest safeplaces within 1.5km
Here is my function
def distance(lat1, long1, lat2, long2):
R = 6371 # Earth Radius in Km
dLat = math.radians(lat2 - lat1) # Convert Degrees 2 Radians
dLong = math.radians(long2 - long1)
lat1 = math.radians(lat1)
lat2 = math.radians(lat2)
a = math.sin(dLat/2) * math.sin(dLat/2) + math.sin(dLong/2) *
math.sin(dLong/2) * math.cos(lat1) * math.cos(lat2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
d = R * c
return d
Here is my model
class Safeplace(models.Model):
establishment = models.CharField(max_length=250)
address = models.CharField(max_length=250)
suburb = models.CharField(max_length=250)
postcode = models.IntegerField()
state = models.CharField(max_length=250)
type = models.CharField(max_length=250)
latitude = models.DecimalField(decimal_places=6,max_digits=10)
longtitude = models.DecimalField(decimal_places=6,max_digits=10)
Is there a way to run a for loop in my database? I am currently working on Django SQLite. On Views.py, how can i implement the distance function with the user input in my rest api url to find the nearest safeplace and display as REST Api?
What you need is to run a comparison for loop in your views.py. It is pretty difficult to execute but I will try to explain step by step.
assuming you are using that distance(lat, lng, lat2, lng2) function and trying to find the distance within 2km for example.
In views.py
import pandas as pd
class someapiview(ListAPIView):
serializer_class = SafeplaceSerializer
### Now we are creating definition which sorts parameters lng and lat ###
def get_queryset(self):
queryset = Safeplace.Objects.all()
lat = float(self.query_params.get('lag', None)
lng = float(self.query_params.get('lng', None)
### Now, we are reading your api using pandas ###
df = pd.read_json('yourapi') ## yourapi is a url to ur api
obj = []
for x in range(0, len(df)):
latx = float(df['latitude'][x])
lngx = float(df['longitude'][x])
### Calculating distance ###
km = distance(lat, lng, latx, lngx)
if km <= 2:
obj.append(df['id'][x])
### Django auto generate primary key which usually calls id ###
### Now we are going to call those pk as a queryset ###
return Safeplace.objects.filter(pk__in=obj)
I used pandas to work around, the load time might be slow if you have lots of data. However, I think this does the job. Usually Geo Django provides an efficient system to deal with long and lat, however I am not very competent in Geo Django so I cannot really tell. But I Believe this is a good work around.
UPDATE :
you can query with "www.yourapi.com/safeplace?lat=x&lng=y"
I believe you know how to set urls

Squeryl - Means of combinations of queries

I have the need to perform some queries that may depend on external supplied parameters via a REST interface. For instance a client may require an URL of the form
some/entities?foo=12&bar=17
The parameters, say foo, bar and quux are all optional. So I start from designing some queries with Squeryl, like
object Entity {
def byFoo(id: Long) = from(DB.entities)(e =>
where(e.foo === id)
select(e)
)
// more criteria
}
But of course I want to avoid the combinatorial explosion that arises, so I only design three queries, which in turn may take their data from another query:
object Entity {
def byFoo(id: Long, source: Query[Entity] = DB.entites) = from(source)(e =>
where(e.foo === id)
select(e)
)
def byBar(id: Long, source: Query[Entity] = DB.entites) = from(source)(e =>
where(e.bar === id)
select(e)
)
// more criteria
}
Now I can combine them and run a query like
val result = Entity.byFoo(12,
source = Entity.byBar(17)
)
The only problem I have with this approach is that behind the scenes Squeryl is going to generate a subquery, which may be inefficient. With a more typical query builder, I would be able to combine the queries and get the equivalent of the following:
from(DB.entities)(e =>
where(
e.foo === 12 and
e.bar === 17
)
select(e)
)
Is there a different way to dynamically combine queries in Squeryl that will lead to this more efficient form?
The first thing I think you should look into is inhibitWhen, which you can find in the documentation. The gist of it is that you model your query like:
var foo: Option[Int] = None
var bar: Option[Int] = None
from(DB.entities)(e =>
where(
e.foo === foo.? and
e.bar === bar.?)
select(e))
The ? operator in the first expression is equivalent to (e.foo === foo).inhibitWhen(foo == None)

MongoDB + Ruby: updating records in an iteration

Using MongoDB and the Ruby driver, I'm trying to calculate the rankings for players in my app, so I'm sorting by (in this case) pushups, and then adding a rank field and value per object.
pushups = coll.find.sort(["pushups", -1] )
pushups.each_with_index do |r, idx|
r[:pushups_rank] = idx + 1
coll.update( {:id => r }, r, :upsert => true)
coll.save(r)
end
This approach does work, but is this the best way to iterate over objects and update each one? Is there a better way to calculate a player's rank?
Another approach would be to do the entire update on the server by executing a javascript function:
update_rank = "function(){
var rank=0;
db.players.find().sort({pushups:-1}).forEach(function(p){
rank +=1;
p.rank = rank;
db.players.save(p);
});
}"
cn.eval( update_rank )
(Code assumes you have a "players" collection in mongo, and a ruby variable cn that holds a conection to your database)

Resources