Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create & Find GeoLocation in mongoose

Tags:

I'm trying to save the longitude/latitude of a location in my user Model and query it with $geoNear. I've looked at almost any page I could find on that topic, but still I'm not sure how to 1) create the model appropriately and how to query it.

here's the part of my model:

loc: {
        //type: 'Point', // see 1
        //type: String, // see 2
        coordinates: [Number],
        index: '2dsphere' // see 3
    },

@1: According the documentation the type must be 'Point' if I want to store a point, but it throws

TypeError: Undefined type `Point` at `loc`
  Did you try nesting Schemas? You can only nest using refs or arrays.

which makes sense, since I guess it should be a String

@2: this doesn't immediately throw an error, but when I try to store a user I get:

name: 'ValidationError',
  errors: 
   { loc: 
      { [CastError: Cast to String failed for value "[object Object]" at path "loc"]

@3: if I want to create the index like that I get the error:

TypeError: Undefined type `2dsphere` at `loc.index`
  Did you try nesting Schemas? You can only nest using refs or arrays.

so I tried to store the index to what felt a little bit more appropriate to me like this:

userSchema.index({'loc': '2dsphere'});

which throws the following error when I try to save a new user:

- { [MongoError: Can't extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }]
  name: 'MongoError',
  message: 'Can\'t extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }',
  ok: 1,
  n: 0,
  code: 16572,
  errmsg: 'Can\'t extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }',
  writeConcernError: 
   { code: 16572,
     errmsg: 'Can\'t extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }' } }
MongoError: Can't extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }

When I updated my model on the database to to a $geoNear command I updated my user like this:

loc: {coordinates: [-73.97, 40.77]}

and could successfully query it like this:

 mongoose.model('users').db.db.command({
    "geoNear": users,
    "near": [
        <latitude>,
        <longitude>
    ],
    "spherical": true,
    "distanceMultiplier": 6378.1, // multiplier for kilometres
    "maxDistance": 100 / 6378.1, // every user within 100 km
    "query": { }

(I used db.command since from what I've read $geoNear is not available when using find())

Everything I tried so far got me nowhere, so my question now is:

  • how should my mongoose model look like?
  • how can I store a user with his or her location in my database

Any help would be much appreciated!

like image 758
Markus Avatar asked Aug 25 '15 08:08

Markus


People also ask

What is the synonym of create?

build, conceive, constitute, construct, design, devise, discover, establish, forge, form, found, generate, initiate, invent, make, organize, plan, produce, set up, shape.

What creat means?

Definition of creat (Entry 1 of 2) : an East Indian herb (Andrographis paniculata) having a juice that is a strong bitter tonic variously used in local medicine. creat- combining form. variants: or creato-

What type of verb is create?

create used as a verb: To design, invest with a new form, shape etc. To be creative, imaginative.


1 Answers

If you want a schema to support GeoJSON, the you first need to construct that properly:

var userSchema = new Schema({     loc: {         type: { type: String },         coordinates: [Number],     } }); 

That makes sure there is no confusion with the "type" keyword of the schema defintition. If you really want to support the whole range of GeoJSON types, then you can make that a bit looser:

var userSchema = new Schema({     loc: {         type: { type: String },         coordinates: []     } }); 

Next you want to tie an index to the shema:

userSchema.index({ "loc": "2dsphere" }); 

Then of course define a model and store things correctly:

var User = mongoose.model( "User", userSchema );   var user = new User({       "loc": {           "type": "Point",          "coordinates": [-73.97, 40.77]      }  }); 

Noting that your data must be in longitude then latitude order as supported by GeoJSON and all MongoDB geospatial query forms.

Next, rather than dig into obscure usages of database commands directly on the raw driver method, use things instead that are directly supported, and better. Such as $geoNear for the .aggregate() method:

User.aggregate(     [         { "$geoNear": {             "near": {                 "type": "Point",                 "coordinates": [<long>,<lat>]             },             "distanceField": "distance",             "spherical": true,             "maxDistance": 10000         }}     ],     function(err,results) {      } ) 

And now because the data is GeoJSON, the distances are already converted to meters so there is no need to do other conversion work.

Also note that is you have been messing around with this, unless you drop the collection any index you tried will still be there and that will likely cause problems.

You can drop all indexes from the collection in the mongodb shell with ease:

db.users.dropIndexes(); 

Or since you likely need to do some data re-shaping, then drop the collection and start again:

db.users.drop(); 

Set things up properly and you will have no problems.

like image 135
Blakes Seven Avatar answered Oct 11 '22 20:10

Blakes Seven