Mongoose Geo Near Search - How to sort within a given distance?
In find query you need to use location
instead of location.coordinates
.
router.get("/test", async (req, res) => { const lat = 59.9165591; const lng = 10.7881978; const maxDistanceInMeters = 1000; const result = await model .find({ location: { $near: { $geometry: { type: "Point", coordinates: [lng, lat], }, $maxDistance: maxDistanceInMeters, }, }, }) .sort("-score"); res.send(result);});
For $near to work you need an 2dsphere index on the related collection:
db.collection.createIndex( { "location" : "2dsphere" } )
In mongodb $near docs it says:
$near sorts documents by distance. If you also include a sort() for the query, sort() re-orders the matching documents, effectively overriding the sort operation already performed by $near. When using sort() with geospatial queries, consider using $geoWithin operator, which does not sort documents, instead of $near.
Since you are not interested in sorting by distance, as Nic indicated using $near is unnecessary, better to use $geoWithin like this:
router.get("/test", async (req, res) => { const lat = 59.9165591; const lng = 10.7881978; const distanceInKilometer = 1; const radius = distanceInKilometer / 6378.1; const result = await model .find({ location: { $geoWithin: { $centerSphere: [[lng, lat], radius] } }, }) .sort("-score"); res.send(result);});
To calculate radius we divide kilometer to 6378.1, and miles to 3963.2 as described here.
So this will find the locations inside 1km radius.
Sample docs:
[ { "location": { "type": "Point", "coordinates": [ 10.7741692, 59.9262198 ] }, "score": 50, "_id": "5ea9d4391e468428c8e8f505", "name": "Name1" }, { "location": { "type": "Point", "coordinates": [ 10.7736078, 59.9246991 ] }, "score": 70, "_id": "5ea9d45c1e468428c8e8f506", "name": "Name2" }, { "location": { "type": "Point", "coordinates": [ 10.7635027, 59.9297932 ] }, "score": 30, "_id": "5ea9d47b1e468428c8e8f507", "name": "Name3" }, { "location": { "type": "Point", "coordinates": [ 10.7635027, 59.9297932 ] }, "score": 40, "_id": "5ea9d4971e468428c8e8f508", "name": "Name4" }, { "location": { "type": "Point", "coordinates": [ 10.7768093, 59.9287668 ] }, "score": 90, "_id": "5ea9d4bd1e468428c8e8f509", "name": "Name5" }, { "location": { "type": "Point", "coordinates": [ 10.795769, 59.9190384 ] }, "score": 60, "_id": "5ea9d4e71e468428c8e8f50a", "name": "Name6" }, { "location": { "type": "Point", "coordinates": [ 10.1715157, 59.741873 ] }, "score": 110, "_id": "5ea9d7d216bdf8336094aa92", "name": "Name7" }]
Output: (within 1km and sorted by descending score)
[ { "location": { "type": "Point", "coordinates": [ 10.7768093, 59.9287668 ] }, "score": 90, "_id": "5ea9d4bd1e468428c8e8f509", "name": "Name5" }, { "location": { "type": "Point", "coordinates": [ 10.7736078, 59.9246991 ] }, "score": 70, "_id": "5ea9d45c1e468428c8e8f506", "name": "Name2" }, { "location": { "type": "Point", "coordinates": [ 10.795769, 59.9190384 ] }, "score": 60, "_id": "5ea9d4e71e468428c8e8f50a", "name": "Name6" }, { "location": { "type": "Point", "coordinates": [ 10.7741692, 59.9262198 ] }, "score": 50, "_id": "5ea9d4391e468428c8e8f505", "name": "Name1" }]
$near
sorts documents by distance which is a waste here. It's probably better to use $geoWithin
which does not sort documents. Something like:
model.find({ "location.coordinates": { $geoWithin: { $center: [ [-74, 40.74], <radius> ] } } }).sort({score: -1});
The $center
docs have some more details.