Mongoose: Where query with geoNear

Created on 17 Jul 2014  路  24Comments  路  Source: Automattic/mongoose

i want to query all user location with geoNear query like this

 Campaign.where('uid','123')
            .geoNear(point, { maxDistance : dist, spherical : true, distanceMultiplier:6378 },
        function(err, data, stats){
            if(err){
                return handleError(res, err);
            }

            return res.json({
                'stats': stats,
                'data': data 
            })
        });

but i get following error

TypeError: Object #<Query> has no method 'geoNear'

i want use geoNear because i need distance property with stats
Can i do this ??
pls Help

Most helpful comment

Here's how im doing it:

property.aggregate().near(
{ 
    near:[parseFloat(req.params.longitude), parseFloat(req.params.latitude)], 
    distanceField:"distance", 
    spherical:true, 
    distanceMultiplier:3959,
    maxDistance:parseFloat(req.params.distance)/3959,
    query: {
        monthlyFee : { 
                                $gte: parseInt(req.params.minPrice), 
                                $lte: parseInt(req.params.maxPrice)
                    },
        numberOfBedrooms : { $gte: parseInt(req.params.minBedrooms) }
    }
})
.exec(function(err,docs) {
    if (err) res.send(err);

    property.populate( docs, { path: 'user', select: 'firstname lastname email' }, function(err,properties) {
        if (err) res.json(err);
        res.json(properties);
    });

});

All 24 comments

@programming-kid that's odd. What version of mongoose are you using? This should theoretically work...

mongoose: ~3.8.8
i tried updating to 3.8.13 but still i get following error

Object #&lt;Query&gt; has no method &#39;geoNear&#39;

Yeah, Query doesn't have geoNear, so you have to use the fluent query syntax instead of .geoNear() if you're going to use .where(), here's some examples from the docs

how to get distance property if i use .where()

Here's how im doing it:

property.aggregate().near(
{ 
    near:[parseFloat(req.params.longitude), parseFloat(req.params.latitude)], 
    distanceField:"distance", 
    spherical:true, 
    distanceMultiplier:3959,
    maxDistance:parseFloat(req.params.distance)/3959,
    query: {
        monthlyFee : { 
                                $gte: parseInt(req.params.minPrice), 
                                $lte: parseInt(req.params.maxPrice)
                    },
        numberOfBedrooms : { $gte: parseInt(req.params.minBedrooms) }
    }
})
.exec(function(err,docs) {
    if (err) res.send(err);

    property.populate( docs, { path: 'user', select: 'firstname lastname email' }, function(err,properties) {
        if (err) res.json(err);
        res.json(properties);
    });

});

Thanks @itaylorweb , not pretty but it gets jjob done.

@programming-kid @vkarpov15

how could I convert:

query = query.where('location.geocode')
            .near({
                center: [params.lng, params.lat],
                maxDistance: params.distance * TO_RADIANS,
                spherical: true
            });

to .aggregate().near(?

My model is:

geocode: {
                type: {type: String, default: 'Point'},
                coordinates: []
            },

Assuming you've created a 2dsphere index on location.geocode?
You can try:

location.aggregate().near(
{ 
    near:[parseFloat(params.lng), parseFloat(params.lat)],     
    spherical:true, 
    distanceMultiplier:3959,
    maxDistance:parseFloat(params.distance)/3959
})
.exec(function(err,docs) {
    if (err) res.send(err);
    res.json(properties);
});

Hey @itaylorweb

Ok, but I got other query.where happening before this filter
ex:

query = query.where('dateOfBirth').lte(now);

Having said that, should I do?

query = location.aggregate().near(
        {
            near: [parseFloat(params.lng), parseFloat(params.lat)],
            spherical: true,
            distanceMultiplier: 3959,
            maxDistance: parseFloat(params.distance)/3959
        });

The "coordinates" array is holding 2 values (long, lat)

Just build your query into the aggregate:
geocode in your model is a GeoJSON object so aslong as you've configured a 2dsphere index on geocode it should work.

var location = require('path to your location model');

location.aggregate().near(
{ 
    near:[parseFloat(params.lng), parseFloat(params.lat)],     
    spherical:true, 
    distanceMultiplier:3959,
    maxDistance:parseFloat(params.distance)/3959,
    query: {
        dateOfBirth : { 
            $lte: now
        }
    }
})
.exec(function(err,docs) {
    if (err) res.send(err);
    res.json(docs);
});

Example location schema:

var locationSchema   = new Schema({
    geocode: {
        type: {type: String, default: 'Point'},
        coordinates: []
    },
    dateOfBirth: Date
});

locationSchema.index({ geocode: '2dsphere' });

module.exports = mongoose.model('Location', locationSchema);

My only concerne is that I have a bunch of "query.where()" before the geo search.

query = User.find();
query.where()
query.where()
query.where()

The only way to make it work would be removing all the ".where" statements... Therefor making a single aggregation query?

Btw, thanks a lot for the help

I think thats what you need to do. Post the full code if you want. It may be straight forward.

No worries... It is just because is gonna be a big refectory process. I have 18 parameters, but that is fine. Thanks a lot @itaylorweb
I will test the aggregate now

Hey @itaylorweb

Should the following query do the job?

Model:

userSchema = new Schema({
        name: String,
        phone: String,
                 ......
        location: {
            city: String,
            state: String,
            country: String,
            geocode: {
                type: {type: String, default: 'Point'},
                // This is a length 2 array with longitude, latitude
                coordinates: []
            },
        },
                ....

Query:

var newQuery =
        User.aggregate().near(
        {
            near: [parseFloat(params.lng), parseFloat(params.lat)],
            spherical: true,
            distanceMultiplier: 3959,
            maxDistance: parseFloat(params.distance)/3959
        });

newQuery.exec().then(function (err, docs) {
    console.log('ERR: ' + err);
    console.log('Docs: ' + docs);
});

Btw, I do have a 2dsphere index on location.geocode... and my previous filter was working:

query = query.where('location.geocode')
            .near({
                center: [params.lng, params.lat],
                // The distance is in radians: (actual travel distance) / earth_radius
                maxDistance: params.distance * TO_RADIANS,
                spherical: true
            });

Looks ok to me. Have you ran it?

It didnt. :/

what error do you get?

Not sure if the .then is having an effect?

newQuery.exec(function (err, docs) {
    console.log('ERR: ' + err);
    console.log('Docs: ' + docs);
});

got it!

User.aggregate().near(
        {
            near: [params.lng, params.lat],
            // The distance is in radians: (actual travel distance) / earth_radius
            maxDistance: params.distance * TO_RADIANS,
            spherical: true,
            distanceField: 'distance'

        });

Now it is working!

Glad it's working. Yep I have a distance field defined on my model: distance: Number, // Used to store distance when required

Thanks a lot @itaylorweb !

Just a quick note.

If you have query.where statements being executed before, you can get the generated condition list with " query._conditions".

query = query.where('location.city').equals(params.city);
query = query.where('location.country').equals(params.country);

query = User.aggregate().near(
            {
                near: { type: 'Point', coordinates: [params.lng, params.lat] },
                distanceField: 'dist.calculated',
                maxDistance: params.distance * TO_RADIANS,
                query: query._conditions,
                spherical: true
            });
Was this page helpful?
0 / 5 - 0 ratings

Related issues

lukasz-zak picture lukasz-zak  路  3Comments

adamreisnz picture adamreisnz  路  3Comments

simonxca picture simonxca  路  3Comments

adamreisnz picture adamreisnz  路  3Comments

ghost picture ghost  路  3Comments