I read all posts but don't find solution. I make a .near request with geocoder, and It work for one controller but doesn't work for another one with this error : Wrong number of arguments (3 for 1).
Here is the two same lines :
This one doesn't work in my school_controller.rb
#schools_premium = School.where(:subscription.exists => true)
#schools_aside = #schools_premium.near([params[:latitude], params[:longitude]], radius, units: :km).limit(3)
I try too with : #school.coordinates.reverse instead of params[:latitude] ..
But this one does (in home_controller.rb) :
#schools = School.near([params[:latitude], params[:longitude]], radius, units: :km).limit(30)
I have the geocoded_by and reverse_geocoder on my School model.
Does someone have the same issue ?
You can achieve by this way also:
#schools = School.near([params[:latitude], params[:longitude]], radius, units: :km).limit(30)
#schools_aside = #schools.premium_school
School.rb (model)
scope :premium_school, -> {where("subscription = ?", true)}
Related
I need a RoR mongoDB query to list articles within a given radius, sorted by created_at.
The challenge is that addresses are saved in separate table and referenced by key/id out of articles. Don't know how to make query with geoNear for this scenario.
Also pagination needed and performant query desirable.
Currently approaching like:
Get addresses within defined radius
Get articles associated to address results out of 1.
sort_by address (geoNear default)
Pagination is making usage of last_address_id. Also here have an issue, as last page is in loop.
#seaches_controller.rb
def index
#addresses =
Address.get_addresses_with_radius(article_search_params).to_a
#address_hash = #addresses.group_by{|a| a['_id'].to_s}
#articles = Article.includes(:gift, :category)
.where( transaction_status:
{
'$nin' => ["concluded"]
},
address_id:
{
:$in => #addresses.map{|a| a['_id'].to_s}
}
).to_a
.sort_by{|m|
#addresses.map{|a|
a['_id']}.index(m['address_id']) }
end
#address.rb
def self.get_addresses_with_radius(params, additional_query={})
#raw query for aggreegate with geoNear
last_maximum_distance = params[:last_maximum_distance] || 0 # in
meeters
radius = params[:radius] || 5000000 #In Meters
query_params = additional_query
if params[:last_address_id]
query_params[:_id] ||= {}
query_params[:_id] = query_params[:_id].merge({ '$ne' =>
(BSON::ObjectId(params[:last_address_id])) } )
end
addresses_in_radius =
Address.collection.aggregate([
{
'$geoNear':
{
near:
{
type: "Point",
coordinates: [ params[:lat].to_f, params[:lon].to_f ]
},
distanceField: "distance_from", #GeoNear Will atomatically distance as distance_from_field
minDistance: last_maximum_distance.to_f,
maxDistance: radius,
query: query_params,
#query:{ 'location.0': {'$ne' =>
params[:last_lat].to_f},'location.1': {'$ne' => params[:last_lon].to_f}},
spherical: true
}
},
{"$limit": params[:per_page].to_i}
])
addresses_in_radius
end
Currently I'm getting the list of articles sorted by addresses/distance, as per default geoNear behavior => should be by created_at.
Pagination is somehow based on addresses => should ideally be based on articles.
Pagination is buggy, as last page is loading in loop => loop-bug to go away.
Not sure if best is to first search for articles and then addresses, or first addresses and then get the articles; relevant note: all within defined radius.
Controller
def index
markers = Marker.nearby(params[:lat], params[:lng], params[:radius])
end
Model
def self.nearby(lat, lng, radius)
approved.near([lat.to_f, lng.to_f], radius, units: :km)
end
Routes:
resources :markers
URL:
http://localhost:3000/markers?lat=11111111&lng=10101010101&radius=20
Notes:
In the URL below we have 3 paramaters(lat, lng and radius). and i want the radius value equal to 1 (1 km). so when the user open (http://localhost:3000/markers?lat=11.111111&lng=104.1010101) it'll get the default value with radius=1 (http://localhost:3000/markers?lat=11.111111&lng=104.1010101&radius=1).
I'd suggest setting the default value for radius in the model method. That ensures a consistent behavior even if you run it from the console or not using request parameters.
def self.nearby(lat, lng, radius)
approved.near([lat.to_f, lng.to_f], radius || 1, units: :km)
end
Then Marker.nearby(11.111, 104.010101, nil) should set radius value to 1. Please notice this will only work when radius is nil, will fail for empty strings. if that's a concern you should check for present? like:
def self.nearby(lat, lng, radius)
radius = radius.present? ? radius : 1
approved.near([lat.to_f, lng.to_f], radius, units: :km)
end
You can use fetch.
Marker.nearby(params[:lat], params[:lng], params.fetch(:radius, 1))
I have two queries, I need an or between them, i.e. I want results that are returned by either the first or the second query.
First query is a simple where() which gets all available items.
#items = #items.where(available: true)
Second includes a join() and gives the current user's items.
#items =
#items
.joins(:orders)
.where(orders: { user_id: current_user.id})
I tried to combine these with Rails' or() method in various forms, including:
#items =
#items
.joins(:orders)
.where(orders: { user_id: current_user.id})
.or(
#items
.joins(:orders)
.where(available: true)
)
But I keep running into this error and I'm not sure how to fix it.
Relation passed to #or must be structurally compatible. Incompatible values: [:references]
There is a known issue about it on Github.
According to this comment you might want to override the structurally_incompatible_values_for_or to overcome the issue:
def structurally_incompatible_values_for_or(other)
Relation::SINGLE_VALUE_METHODS.reject { |m| send("#{m}_value") == other.send("#{m}_value") } +
(Relation::MULTI_VALUE_METHODS - [:eager_load, :references, :extending]).reject { |m| send("#{m}_values") == other.send("#{m}_values") } +
(Relation::CLAUSE_METHODS - [:having, :where]).reject { |m| send("#{m}_clause") == other.send("#{m}_clause") }
end
Also there is always an option to use SQL:
#items
.joins(:orders)
.where("orders.user_id = ? OR items.available = true", current_user.id)
You can write the query in this good old way to avoid error
#items = #items.joins(:orders).where("items.available = ? OR orders.user_id = ?", true, current_user.id)
Hope that helps!
Hacky workaround: do all your .joins after the .or. This hides the offending .joins from the checker. That is, convert the code in the original question to...
#items =
#items
.where(orders: { user_id: current_user.id})
.or(
#items
.where(available: true)
)
.joins(:orders) # sneaky, but works! 😈
More generally, the following two lines will both fail
A.joins(:b).where(bs: b_query).or(A.where(query)) # error! 😞
A.where(query).or(A.joins(:b).where(bs: b_query)) # error! 😞
but rearrange as follows, and you can evade the checker:
A.where(query).or(A.where(bs: b_query)).joins(:b) # works 😈
This works because all the checking happens inside the .or() method. It's blissfully unaware of shennanigans on its downstream results.
One downside of course is it doesn't read as nicely.
I ran into the same issue, however the code was defined in a different place and was very difficult to change directly.
# I can't change "p"
p = Post.where('1 = 1').distinct # this could also be a join
And I needed to add an or statement to it
p.or(Post.where('2 = 2'))
The following code won't raise an error, because it has distinct like the initial relationship.
p.or(Post.where('2 = 2').distinct)
The problem with it it that it only works as long as you know the relationship. It may or not have a join, or distinct.
This works regardless of what the relationship is:
p.or(p.unscope(:where).where('2 = 2'))
=> SELECT DISTINCT `posts`.* FROM `posts` WHERE ((1 = 1) OR (2 = 2))
It occurs when you try to combine two multi-active records of the same type, but one of them has a joins value or an includes value, or in your case a reference value, that the other does not.
Therefore we need to match the values between them, and I found a general way to do this without knowing the actual values in advance.
items_1 = #items.joins(:orders)
.where(orders: { user_id: current_user.id})
items_2 = #items.where(available: true)
.joins(items_1.joins_values)
.includes(items_1.includes_values)
.references(items_1.references_values)
#items = items_1.or(items_2)
just solve it!
def exec_or_statement(q1, q2)
klass = q1.klass
key = klass.primary_key
query_wrapper_1 = {}
query_wrapper_1[key] = q1
query_wrapper_2 = {}
query_wrapper_2[key] = q2
klass.where(query_wrapper_1).or(klass.where(query_wrapper_2))
end
query_1 = #items.where(available: true)
query_2 =
#items
.joins(:orders)
.where(orders: { user_id: current_user.id})
exec_or_statement(query_1, query_2)
when trying to create a class with a single argument, I am getting the following error: NameError: undefined local variable or method radius' for #<Circle:0x007fdcda2b75c8> from circle_constructor.rb:13:ininitialize'
class Circle
def initialize(circle_constructor = {})
circle_constructor = {#radius => radius, #diameter => diameter}
#radius = radius
#diameter = diameter
end
end
if i understand you correctly you try to initialize #radius and #diameter with the hash you got as a parameter so try the following:
class Circle
def initialize(circle_constructor = {})
## circle_constructor = {#radius => radius, #diameter => diameter}
## you are inserting wrongly values to local variable inside constractor wothout doing nothing with it so remove it or do this:
## #circle_constructor = { radius: radius, diameter: diameter }
## make sure you populate radius and diameter local variables before.
#radius = circle_constructor[:radius]
#diameter = circle_constructor[:diameter]
end
end
Ruby doesn't support this out of the box, but you can use Hashie library for this. It has several hash variants and dash would work well for this.
require 'hashie/dash'
class Circle < Hashie::Dash
property :radius, required: true
property :diameter, required: true
end
# USAGE
Circle.new(radius: 10, diameter: 5)
(On a side note, it's odd that your circle accepts both a radius and a diameter. Unless it's user input such as a quiz, you should be able to derive one from the other, so it would normally only have one constructor param and the other would be a method.)
I'm trying to make an active record that involves location. I get the longitude from the params, and try to use the Float() method to convert the locationLongitude and locationLatitude from a string to a float, and I get the following error:
undefined method `call' for #<Class:0x007ffead288530>
Here are the params that the method has access to:
{"locationName"=>"Stanford",
"locationLatitude"=>"37.42839679991957",
"locationLongitude"=>"-122.17553785073696"}
And this is the method in my controller that attempts to convert the strings into floats and make the query:
def local
radius = 10;
#sort = "local"
#locationName = params[:locationName]
#locationLongitude = Float(params[:locationLongitude])
#locationLatitude = Float(params[:locationLatitude])
#musings = Musing.(:longitude => (Float(#locationLongitude) - radius)..(Float(#locationLongitude) + radius))
end
Hope you can help. I also tried doing params[:locationName].to_f and that also didn't work.
Thanks, Paul.
I'd say it's better to move the processing from within your local method to the Musing (or other) model.
In your form - try to namespace your parameters such that it'd have a musing as an outer most one.
<input name='musing[locationName' ...>
In the controller
def local
# set some vars
#musings = Musing.search(params[:musing])
end
In the model
def self.search(params)
radius = 10
long = params[:locationLongitude]
lat = params[:locationLatitude]
return self unless long and lat
self.where(:latitude => lat.to_f-radius).where(:long => long.to_f-radius)
end
I can see you resolved the issue - but this might help
Please, change
params[:locationName].to_f
to
params[:locationName].to_s.to_f
The issue was in this line:
#musings = Musing.(:longitude => (Float(#locationLongitude) - radius)..(Float(#locationLongitude) + radius))
I wrote Musing.(...) instead of Musing.where(...)
What a scrub.