Does MongoDB provide the distance to point with $nearSphere operator? - mongodb

The $nearSphere MongoDB operator is able to return the documents near to a given point (specified as parameter of the operator) sorted by incresing distance.
However, how to get the actual distance? Is there any mean of getting this using MongoDB functionality? Either with $nearSphere (although I have checked its documentation and I haven't found this information) or with other mechanisms (maybe the aggregation framework?)
Thanks!

With aggregation you can use the $geoNear stage to provide both spherical geometry and return the distance.
Example from the docs:
db.places.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [ -73.99279 , 40.719296 ] },
distanceField: "dist.calculated",
maxDistance: 2,
query: { category: "Parks" },
includeLocs: "dist.location",
spherical: true
}
}
])

Related

MongoDB $near and $geoNear gives location with huge difference from real one

i noticed that mongoDB $near and $geoNear returns around 1km bigger distance than should be.
Point1 (current location):
lat: 54.8985,
long: 23.9036,
Point2 (location in DB):
"location" : {
"type" : "Point",
"coordinates" : [
54.91689,
23.97423
]
}
based on multiple libraries distance should be around 4.7KM
however mongodb returns: "calcDistance" : 8082 = 8km
DIFFERENCE is 4 KM!
You can check it here:
I have tried both spherical and non-spehrical and tried both $geowNear and $near
my code:
db.getCollection('collection').aggregate([
{
$geoNear: {
near: { type: 'Point', coordinates: [ 54.8985, 23.9036 ] },
distanceField: 'calcDistance',
maxDistance: 5 * 1000,
}
}
])
Any ideas why?
Look slike $near and $geoNear need lat and long swapped. That was a problem.
Refer to this: geoNear returns incorrect distance

Mongo geoNear Aggregation Pipeline - 'near' field must be point

I am using a Mongo pipeline within an aggregation lookup on 2 collections, Locations and Places.
I am trying to return all the places which these locations are near.
The error I get is 'MongoError: 'near' field must be point'
I believe this is because I am trying to use the $point variable in the pipeline from the let in the lookup and I am doing something wrong here. All the answers I see on here have static coordinates but I want to use the ones from the lookup.
This is the code I have:
return await this.placeModel.aggregate([{
$lookup : {
from : "locations",
let : {point : "location.coordinates"},
pipeline: [ {
$geoNear: {
distanceField: "distance",
near: { type: "Point", coordinates: "$point" },
maxDistance: 20,
spherical: true
}
}],
as : "places"
}
}]);
}
I have a mongoose Place model and Location model. Each model has a GeoJson point that looks like this:
location: {
type: { type: String },
coordinates: []
},
How do I reference the point properly if at all possible.

MongoDB $near query accuracy issue

In my application I would like to query items that are close by (e.g. within 5km) to a coordinate and I tried to use $near to achieve that. With a quick look I thought it worked but after I tested it further it seems the query is somewhat inaccurate. Here is my setup:
I selected 2 coordinates that are a bit less than 5km apart from each other:
61.4644750214197, 23.8426943813556
61.497133399999996, 23.778528100000003
(At least according to tools like this, this or this the distance between those coordinates should be about ~4,99km)
I added one of the coordinates into empty "items" collection:
db.items.insert({
"geo" : {
"type" : "Point",
"coordinates" : [
61.4644750214197,
23.8426943813556
]
}
});
I added "2dsphere" index to the collection make geospatial queries possible:
db.items.createIndex( { geo : "2dsphere" } )
Finally, I used the other coordinate with $near query:
db.items.find({geo: {
$near: {
$geometry: {
type: "Point" ,
coordinates: [ 61.497133399999996, 23.778528100000003 ]
},
$maxDistance: 5000 // according to docs with '2dsphere' index and GeoJSON this is is meters
}
}}).count()
I expected the result to be 1 but instead it is 0. Even if I set $maxDistance to 7000 the result is still 0, but if I set it to 8000 the result will be 1.
Am I doing something wrong or are MongoDB geospatial queries (or just $near query?) that inaccurate? If so, is there a better way to get accurate results for this kind of query? This is my first time dealing with geospatial queries in MongoDB so there is probably a trivial explanation for my problem.
EDIT:
Basically I was dreaming of a functionality to show all items in map within X kilometres from users current location and X could be determined by user. It would be awkward if an item within 5km would not be visible even when the user wants to filter items within 7km.
I have tried most of the options for doing this query, like $centerSphere, $nearSphere and geoNear with similar results. They all seem to claim that the distance between my earlier mentioned coordinates is somewhere between 7-8km. I'm starting to think either 1. I'm missing some key peace of information about how distances work in general or 2. it simply is not possible to solve my problem with mongodb. Below are my queries for the other options:
$centerSphere (0 results with 5, 6 and 7km but 1 result with 8km):
db.items.find( { geo: {
$geoWithin: { $centerSphere: [ [ 61.497133399999996, 23.778528100000003 ], 5/6378.1 ]
}
}}).count()
geoNear (0 results with maxDistance 5000, 6000 and 7000 but 1 result with 8000):
db.runCommand(
{
geoNear: "items",
near: { type: "Point", coordinates: [ 61.497133399999996, 23.778528100000003 ] },
spherical: true,
maxDistance: 5000
}
)
I understand I am late to the party, but for all those who are facing similar issue
The problem here is that when you store that data into "coordinates", it must be in the [longitude, latitude] order because this is how mongodb works. https://docs.mongodb.com/manual/geospatial-queries/#spherical I just ran your example with reversed order of coordinates and it worked as expected.
db.items.insert({
"geo" : {
"type" : "Point",
"coordinates" : [
23.8426943813556,
61.4644750214197
]
}
});
And then i ran
db.items.find({geo: {
$near: {
$geometry: {
type: "Point" ,
coordinates: [ 23.778528100000003 , 61.497133399999996]
},
$maxDistance: 5000
}
}}).count()
The count here is 1:
Hope it helps

MongoDB $geoNear operation "near" field usage

I have a collection "machine" with document
{
"_id" : "ac9d1db9-6a0d-47c6-97d3-a613c8dd0031",
"pin" : {
"machine":"test1",
"position" : [
-1.5716,
54.7732
]
}
}
Note: -1.5716 is lng and 54.7732 is lat
I have created a 2dsphere index on the document
db.machine.createIndex( { 'pin.position' : "2dsphere" } )
I try with 2 different versions of query (only difference in query is the near field in geoNear pipeline stage)
Query 1:
db.machine.aggregate(
[
{
"$geoNear":{
"near": [-0.2129092,51.5031594],
"limit":100,
"maxDistance":500*1000,
"distanceField": "dist.calculated",
"includeLocs": "dist.location",
"distanceMultiplier":1/1000,
"spherical": true
}
}
]
)
Note: -0.2129092 is lng and 51.5031594 is lat
Query 2
db.machine.aggregate(
[
{
"$geoNear":{
"near": { type: "Point", coordinates: [-0.2129092,51.5031594] },
"limit":100,
"maxDistance":500*1000,
"distanceField": "dist.calculated",
"includeLocs": "dist.location",
"distanceMultiplier":1/1000,
"spherical": true
}
}
]
)
Note: -0.2129092 is lng and 51.5031594 is lat
Query 1 returns me the document and provides that this document is 5.88161133560063e-05 Kms away from the search co-ordinates
Query 2 returns me the document and provides that this document is 375.135052595944 Kms away from the search co-ordinates
I cross-verify the distance between these lng/lat on a site http://andrew.hedges.name/experiments/haversine/ and see that the distance between the document and the search co-ordinates is around 374.835 Kms
It seems Query 2 is providing the correct result but am not sure as to what is the difference between Query 1 and Query 2 and if I am using it incorrectly.
Please advise
Query 1 provides the distance in legacy co-ordinate pairs and Query 2 provices the distance in meters (GeoJSON) and hence both queries are using different units
Please check the following link https://jira.mongodb.org/browse/SERVER-16652?jql=text%20~%20%22geoNear%22

mongodb with $match, $skip, $limit and $geoNear

I wanted to do this:
model.User.aggregate([
//step 1 match criteria
{
$match: criteria
},
//step 2 skip
{
$skip: offset
},
//step 3 limit
{
$limit: limit
},
//step 4 sort by computed distance
{
$geoNear : {
near: {type: 'Point', coordinates: coords },
distanceField: 'currentCity.computed_distance',
includeLocs: 'currentCity.loc',
spherical: true,
uniqueDocs: true,
distanceMultiplier: 3963.2, //convert to miles (this number is the radius of the earth in miles)
}
}
],function(err,users){
if (err) return res.error(err);
if (!users.length) return res.error('no matched criteria');
res.apiResponse(users);
});
but the documentation of $geoNear states:
You can only use $geoNear as the first stage of a pipeline.
Reading the documentation, i see that i can simply move $match inside of $geoNear via the query option. likewise, $limit may be placed inside of $geoNear via the limit option. the one problem is, there is no equivalent for the $skip option, so it looks as if facilitating pagination would not be possible? i'm really confused here, why $geoNear can't be the 4th step in the pipeline. The goal of the query is simply to find the best n matches, where n = limit, then sort by nearest in proximity. is this even possible? I am having trouble finding an answer for this specific use case.
I suppose one solution might be to perform a query to select only ids matching documents, convert to list of ids, then do the aggregates with an $in query like so:
model.User.find(criteria).skip(offset).limit(limit).select('_id').exec(function (err, userIds) {
var ids = [];
userIds.forEach(function(u){
ids.push(u._id);
});
model.User.aggregate([
{
$geoNear : {
query: { _id: {$in: $ids } },
near: {type: 'Point', coordinates: coords },
distanceField: 'currentCity.computed_distance',
includeLocs: 'currentCity.loc',
spherical: true,
uniqueDocs: true,
distanceMultiplier: 3963.2, //convert to miles (this number is the radius of the earth in miles)
}
}
],function(err,users){
if (err) return res.error(err);
if (!users.length) return res.error('no matched criteria');
res.apiResponse(users);
});
});
This would work, but ideally i could do it in 1 query if possible. any ideas greatly appreciated.
One solution is this one:
result = db.cafes.aggregate([{
'$geoNear': {
'near': {
'type': 'Point',
'coordinates': [
-73.991084,
40.735863]},
'spherical': True,
'distanceField': 'dist',
'num': 20}
}, {
'$skip': 10
}])
There is also a better solution with this approach:
ids = [42]
result = db.command(
'geoNear', 'cafes',
near={
'type': 'Point',
'coordinates': [
-73.991084,
40.735863]},
spherical=True,
minDistance=268,
query={
'_id': {
'$nin': ids}},
num=10)
And a really nice explanation on speed and issues over here:
https://emptysqua.re/blog/paging-geo-mongodb/