Rails Mongoid geo_near sort by distance - ruby-on-rails
What I'm trying to do is to sort mongoid geo_near(or within_circle) max_distance results by distance because I don't know why but it doesn't do that by default.
I have mongoid_geospatial, mongoid_spacial and rgeo in my Rails gem file. I know mongoid_spacial has this ability but I could not use it and it caused problems with gmaps4rails.
I'm trying on mongoid_geospatial(which uses mongoid libraries) with no success and I could not find any resource except this one https://stackoverflow.com/questions/18633636/mongodb-aggregate-geonear-maxdistance but I don't know how to convert mongo that to mongoid.
Any one had an experience on sorting geo_near or within_circle in mongoid? Any help will be greatly appreciated.
The code in my Controller
searchterm = session[:categoryid].to_s
radius = session[:distance].to_f / 10
#places = Provider.all.where(:category.to_s => /.*#{searchterm}.*/)
.geo_near([ session[:latitude].to_f,session[:longitude].to_f ]).max_distance(radius)
##places = Provider.all.where(:category.to_s => /.*#{searchterm}.*/)
#.within_circle(location: [[ session[:latitude].to_f,session[:longitude].to_f ], radius ])
#.sort(:servicescore.desc).sort(:pricescore.desc)
The code in my model
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
include Mongoid::Geospatial
include Mongoid::MultiParameterAttributes
include Mongoid::Slug
include Mongoid::Spacial::Document
include Gmaps4rails::ActsAsGmappable
acts_as_gmappable :lat => 'latitude', :lon => 'longitude', :process_geocoding => true,
:check_process => :prevent_geocoding,
:address => "business_address"
field :location, :type => Point
spatial_index :location
field :officialname
field :business_description
field :category#, :type => Array
field :business_type
field :tax_office
field :tax_number
field :pin
field :business_phone
field :web_site
field :business_address
field :latitude
field :longitude
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
EDIT ON QUESTION
I've tried almost anything related without success. Read mongo and mongoid geo_near documentation. It does comment on storing location [lon, lat] order. I've tried both [lon, lat] and [lat, long] both when saving the provider and for the center location. Tried spherical geo_near and within_circle too. Still without success.
Geo_near returns my results incorrectly ordered as seen on the map above and on the list below. There should be something I'm missing but can't find it.
The red parts are the "geo_near_distance" output results, the list is sorted according to those but they are wrong as seen on the map. The green calculation is jquery code I've been using that returns correct distances(though cannot use in controller).
There should be something I'm missing. Can anyone spot that?
here is my latest code:
Provider.rb
field :location, :type => Array # [lat,lng]
index({ location: "2d" }, { min: -180, max: 180 })
spatial_index :location
Providers_controller
center = [session[:longitude].to_f, session[:latitude].to_f]
radious = 100
searchterm = session[:categoryid].to_s
radius = session[:distance].to_f / 10
#places = Provider.all.where(:category.to_s => /.*#{searchterm}.*/)
.geo_near(center).max_distance(radious)#.spherical
#.near(location: center)
#.within_circle( location: [center,radius] )
#.sort(:geo_near_distance.desc)
#.geo_near([ session[:latitude].to_f,session[:longitude].to_f ]).max_distance(100).spherical
#.unit("km")
#.geo_near([ session[:latitude].to_f,session[:longitude].to_f ], max_distance: radius, unit: "km".to_sym, spherical: true).sort_by!{|r| r.geo[:distance] }
#.geo_near([ session[:latitude].to_f,session[:longitude].to_f ]).max_distance(radius)
#.sort(:geo_near_distance)
JQuery for the green part
var p1 = new LatLon(Geo.parseDMS($('#lat1_{{id}}').val()), Geo.parseDMS($('#lon1_{{id}}').val()));
var p2 = new LatLon(Geo.parseDMS($('#lat2_{{id}}').val()), Geo.parseDMS($('#lon2_{{id}}').val()));
var new_number = parseFloat(p1.distanceTo(p2)).toFixed(2);
$('#result-distance_{{id}}').html(new_number+' km');
Data for Provider
{ "_id" : ObjectId("5295ef0fdd5063ce1600002a"), "workdonecount" : 0, "pricescore" : 0, "servicescore" : 0, "_slugs" : [ "5295ef0fdd5063ce1600002a-1" ], "officialname" : "çeşme", "business_description" : "dsadsa", "category" : "5280ad334b315241af406c79", "business_type" : "Ticari", "tax_office" : "dads", "tax_number" : "dss", "pin" : "", "business_address" : "Balçova/İzmir, Türkiye", "latitude" : 38.3692939, "longitude" : 27.093442, "pink" : false, "hizmetkutusu_verified" : false, "user_id" : ObjectId("52946f54dd50636df6000002"), "location" : [ 27.093441999999982, 38.3692939 ], "gmaps" : true, "updated_at" : ISODate("2013-11-27T13:09:36.214Z"), "created_at" : ISODate("2013-11-27T13:09:36.214Z") }
{ "_id" : ObjectId("5295f248dd5063ce16000030"), "workdonecount" : 0, "pricescore" : 0, "servicescore" : 0, "_slugs" : [ "5295f248dd5063ce16000030-1" ], "officialname" : "izmirdsads", "business_description" : "dsdsadsdsa", "category" : "5280ad334b315241af406c79", "business_type" : "Ticari", "tax_office" : "dasds", "tax_number" : "dadsa", "pin" : "", "business_address" : "Çeşme, Türkiye", "latitude" : 38.32980999999999, "longitude" : 26.3149209, "pink" : false, "hizmetkutusu_verified" : false, "user_id" : ObjectId("52946f54dd50636df6000002"), "location" : [ 26.31492090000006, 38.32980999999999 ], "gmaps" : true, "updated_at" : ISODate("2013-11-27T13:23:20.423Z"), "created_at" : ISODate("2013-11-27T13:23:20.423Z") }
{ "_id" : ObjectId("5295f260dd5063ce16000032"), "workdonecount" : 0, "pricescore" : 0, "servicescore" : 0, "_slugs" : [ "5295f260dd5063ce16000032-1" ], "officialname" : "dsadsa", "business_description" : "dsadsa", "category" : "5280ad334b315241af406c79", "business_type" : "Ticari", "tax_office" : "dsadsa", "tax_number" : "dsads", "pin" : "", "business_address" : "Çiğli, 35580 İzmir, Türkiye", "latitude" : 38.496303, "longitude" : 27.0603911, "pink" : false, "hizmetkutusu_verified" : false, "user_id" : ObjectId("52946f54dd50636df6000002"), "location" : [ 27.06039110000006, 38.496303 ], "gmaps" : true, "provider_image" : "gmaps1.jpg", "updated_at" : ISODate("2013-11-27T13:23:44.686Z"), "created_at" : ISODate("2013-11-27T13:23:44.686Z") }
{ "_id" : ObjectId("5295eee3dd5063ce16000026"), "workdonecount" : 0, "pricescore" : 0, "servicescore" : 0, "_slugs" : [ "5295eee3dd5063ce16000026-1" ], "officialname" : "dsdsa", "business_description" : "dadsa", "category" : "5280ad334b315241af406c79", "business_type" : "Ticari", "tax_office" : "dsa", "tax_number" : "dads", "pin" : "", "business_address" : "Buca, İzmir, Türkiye", "latitude" : 38.38813400000001, "longitude" : 27.1753358, "pink" : false, "hizmetkutusu_verified" : false, "user_id" : ObjectId("52946f54dd50636df6000002"), "location" : [ 27.291124800000034, 38.3337361 ], "gmaps" : true, "updated_at" : ISODate("2013-11-27T13:08:51.544Z"), "created_at" : ISODate("2013-11-27T13:08:51.544Z") }
{ "_id" : ObjectId("5295eef5dd5063ce16000028"), "workdonecount" : 0, "pricescore" : 0, "servicescore" : 0, "_slugs" : [ "5295eef5dd5063ce16000028-1" ], "officialname" : "dsdsdsa", "business_description" : "dadsds", "category" : "5280ad334b315241af406c79", "business_type" : "Ticari", "tax_office" : "dasds", "tax_number" : "dadsa", "pin" : "", "business_address" : "Bornova, 35100 İzmir, Türkiye", "latitude" : 38.466414, "longitude" : 27.2192191, "pink" : false, "hizmetkutusu_verified" : false, "user_id" : ObjectId("52946f54dd50636df6000002"), "location" : [ 27.219219100000032, 38.466414 ], "gmaps" : true, "updated_at" : ISODate("2013-11-27T13:09:10.122Z"), "created_at" : ISODate("2013-11-27T13:09:10.122Z") }
I used it few months ago. This is how I used it:
class Person
include Mongoid::Document
field :location, :type => Array # [lat,lng]
index( { location: Mongo::GEO2D }, { min: -180, max: 180 })
end
Then you need to generate indexes:
rake db:mongoid:create_indexes
Then you can make the query:
center = [ 1, 10 ]
radious = 100
Person.within_circle( location: [ center, radious ] )
Or use $near query:
Person.geo_near(center).max_distance(radious) # sorted by distance
All information is specified here:
http://mongoid.org/en/mongoid/docs/indexing.html
http://mongoid.org/en/origin/docs/selection.html
http://mongoid.org/en/mongoid/docs/querying.html#geo_near
Good luck!
Related
Extracting a specific part from JSON
Hello I am trying to extract only the id value from a JSON. However if I print(response!["id"]) result = "not created" is outputted. reponse is already in JSON format. Is there something that I am doing wrong? Update 1 [ { "user" : { "last_name" : "test", "email" : "test#test.com", }, "id" : 902, "scale" : 7, "created_at" : "2018-02-24 06:45:33", }, { "user" : { "last_name" : "test", "email" : "test#test.com", }, "id" : 903, "scale" : 7, "created_at" : "2018-02-24 06:45:33", }, { "user" : { "last_name" : "test", "email" : "test#test.com", }, "id" : 904, "scale" : 7, "created_at" : "2018-02-24 06:45:33", }, ]
The best way to access the id of any object from the array of response will be: let id = response[index]["id"] // Here index is the index of the object you want to access.
I am assuming you want to extract ids from this JSON array. You can do this using response.map({ $0["id"]}) Which will give you another array containing only ids
Why my PassBook isn't valid or outdate?
I generate one passbook using this gem in rails and it seen that works but when I open the passbook .pkpass file I see this message: It's in spanish but basically it says that this card isn't valid anymore. Here is my JSON: { "formatVersion" : 1, "passTypeIdentifier" : "{MY PASS ID HERE}", "serialNumber" : "E5982H-I2", "teamIdentifier" : "{MY TEAM ID HERE}", "webServiceURL" : "https://example.com/passes/", "authenticationToken" : "vxwxd7J8AlNNFPS8k0a0FfUFtq0ewzFdc", "barcode" : { "message" : "123456789", "format" : "PKBarcodeFormatPDF417", "messageEncoding" : "iso-8859-1" }, "locations" : [ { "longitude" : -122.3748889, "latitude" : 37.6189722 }, { "longitude" : -122.03118, "latitude" : 37.33182 } ], "organizationName" : "CROCANTICKETS SL", "description" : "Paw Planet Coupon", "logoText" : "Paw Planet", "foregroundColor" : "rgb(255, 255, 255)", "backgroundColor" : "#FF4B33", "coupon" : { "primaryFields" : [ { "key" : "offer", "label" : "Any premium dog food", "value" : "20% off" } ], "auxiliaryFields" : [ { "key" : "expires", "label" : "EXPIRES", "value" : "2016-04-24T10:00-05:00", "isRelative" : true, "dateStyle" : "PKDateStyleShort" } ] } } Any idea? Thanks!
According to the Expiration Keys in the Passbook Package Format Reference check the expirationDate and voided keys. Since you do not have those in your JSON, it might be added by the gem you are using.
mongodb/rails "exception: can't find special index: 2d for:"
i have a rails app where i have some problems with indexes. I search locations by name. First i thought its a problem with the addresses.coords but iam not sure about it. The relevant parts of the search controller: #practices = Practice.published #practices = #practices.where(:"addresses.country" => params[:country].upcase) if params[:country].present? if params[:location].present? && latlng = get_coordinates #practices = #practices.near_sphere(:"addresses.coords" => latlng).max_distance(:"addresses.coords" => get_distance ) end # now find doctors based on resulting practices #doctors = Doctor.published.in("organization_relations.practice_id" => #practices.distinct(:_id)) The complete crash log: Moped::Errors::OperationFailure (The operation: #<Moped::Protocol::Command #length=255 #request_id=646 #response_to=0 #op_code=2004 #flags=[] #full_collection_name="um-contacts.$cmd" #skip=0 #limit=-1 #selector={:distinct=>"practices", :key=>"_id", :query=>{"deleted_at"=>nil, "published_at"=>{"$lte"=>2012-11-05 15:17:14 UTC}, "addresses.country"=>"DE", "addresses.coords"=>{"$nearSphere"=>[13.4060912, 52.519171], "$maxDistance"=>0.01569612305760477}}} #fields=nil> failed with error 13038: "exception: can't find special index: 2d for: { deleted_at: null, published_at: { $lte: new Date(1352128634313) }, addresses.country: \"DE\", addresses.coords: { $nearSphere: [ 13.4060912, 52.519171 ], $maxDistance: 0.01569612305760477 } }" See https://github.com/mongodb/mongo/blob/master/docs/errors.md for details about this error.): app/controllers/search_controller.rb:16:in `index' Thats the result of the indexes, not sure how to query them from the addresses which are embedded via has_many. > db.practices.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.practices", "name" : "_id_" } ] Help would be really appreciated! Edit: Looks like the indexes for adresses.coords arent created, db.system.indexes.find() { "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.users", "name" : "_id_" } { "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.doctors", "name" : "_id_" } { "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.collaborations", "name" : "_i { "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.practices", "name" : "_id_" } but should be created within the practice class: class Practice ... embeds_many :addresses, cascade_callbacks: true, as: :addressable ... field :name, type: String field :kind, type: String field :slug, type: String index({"addresses.coords" => '2d'}, { min: -180, max: 180, background: true }) index({name: 1}) index({slug: 1}, { unique: true }) ... Anyone have an idea why its failing?
try to re-create your indexes. for mongoid: rake db:mongoid:create_indexes
How to make scope from embedded_in that get only first element?
I have many coordinates embedded in place. How to get only first "start" coordinate for each self Place object? Scope is correct idea? I can select only first, last or all of the places with all(is very slow) coordinates, my commented out scope doesn't work. code: class Place include Mongoid::Document field :title, :type => String embeds_many :coordinates # def self.start_coordinate # self.coordinates.first # first = self.coordinates.first ## first = self.find({}, { "coordinates" => { "_id" => firstobj?}}) ## first = self.find({}, { "coordinates" => {}, :limit=>1}) ## self.includes(:coordinates).first ## self.collection.(:coordinates).find_one() ### self.all # end end class Coordinate include Mongoid::Document include Mongoid::Spacial::Document field :coordinates, :type => Array, spacial: true spacial_index :coordinates embedded_in :place, :inverse_of => :coordinate end MongoDB place object: { "_id" : ObjectId( "4ece5a04ca6a175b08000016" ), "coordinates" : [ { "lat" : 51.54983275438141, "lng" : 17.31981750561522, "_id" : ObjectId( "4ece5a04ca6a175b08000002" ) }, { "lat" : 51.55282151156834, "lng" : 17.35552307202147, "_id" : ObjectId( "4ece5a04ca6a175b08000003" ) }, { "lat" : 51.53830285151265, "lng" : 17.39397522045897, "_id" : ObjectId( "4ece5a04ca6a175b08000004" ) } ], "created_at" : Date( 1322146308000 ), "description" : "description", "title" : "test", "updated_at" : Date( 1322154405000 ), "user_id" : ObjectId( "4ecd7d4eca6a175783000010" ) }
Scopes are typically used to filter the result set of whole objects you want based upon some criteria. Instead, it looks like you are trying to select certain attributes of a given document. To do that you should use the only method. This might work, not sure about the exact syntax though: Place.only(":coordinates.0")
How to make projections of selected attributes in mongoid?
I have Coordinate model with many markers embedded_in User model. How to extract attributes without _ids so that on output display only each lng and lat? structure: { "_id" : ObjectId( "4e9f418f1e7bf20fbc000009" ), "name" : "test", "coordinates" : [ { "lng" : 16.86783310009764, "lat" : 52.38353842845282, "_id" : ObjectId( "4e9f418f1e7bf20fbc00000a" ) }, { "lng" : 16.85787674023436, "lat" : 52.40972501601293, "_id" : ObjectId( "4e9f418f1e7bf20fbc00000b" ) }, { "lng" : 16.92276474072264, "lat" : 52.40071858320756, "_id" : ObjectId( "4e9f418f1e7bf20fbc00000c" ) }, { "lng" : 16.90182205273436, "lat" : 52.38270020105396, "_id" : ObjectId( "4e9f418f1e7bf20fbc00000d" ) }, { "lng" : 16.96705337597655, "lat" : 52.410661698108, "_id" : ObjectId( "4e9f418f1e7bf20fbc00000e" ) }, { "lng" : 16.89495559765624, "lat" : 52.42773236584494, "_id" : ObjectId( "4e9f418f1e7bf20fbc00000f" ) } ] } e.g. = debug #user.coordinates.to_json gives: --- ! '[{"_id":"4e9f418f1e7bf20fbc00000a","lat":52.383538428452816,"lng":16.86783310009764},{"_id":"4e9f418f1e7bf20fbc00000b","lat":52.40972501601293,"lng":16.85787674023436},{"_id":"4e9f418f1e7bf20fbc00000c","lat":52.40071858320756,"lng":16.92276474072264},{"_id":"4e9f418f1e7bf20fbc00000d","lat":52.382700201053964,"lng":16.90182205273436},{"_id":"4e9f418f1e7bf20fbc00000e","lat":52.410661698108,"lng":16.967053375976548},{"_id":"4e9f418f1e7bf20fbc00000f","lat":52.42773236584494,"lng":16.894955597656235}]' expected: --- ! '[{"lat":52.383538428452816,"lng":16.86783310009764},{"lat":52.40972501601293,"lng":16.85787674023436},{"lat":52.40071858320756,"lng":16.92276474072264},{"lat":52.382700201053964,"lng":16.90182205273436},{"lat":52.410661698108,"lng":16.967053375976548},{"lat":52.42773236584494,"lng":16.894955597656235}]'
Try using except #user.coordinates.to_json(:except => '_id')
the accepted answer returns data you wished. I would suggest though to use without against mongoid query so you actually do not fetch the data from DB, which is ideal. #user.coordinates.without('_id').to_json(:except => '_id')