Case
I have store the location coordinates in MongoDB collection with 2dsphere index. I want to get the records which included into the specific bounds on map. I have one coordinate point which is fixed with latitude and longitude. I have used the method $geoWithin
with $box
in MongoDB aggregation. I am able to get the data properly.
Problem
I need to add distance field in each record from the one fixed latitude and longitude I have.
{
"$match": {
"location": {
"$geoWithin": {
"$box": [
[
0.024719919885622943,
51.54643953472475
],
[
-0.1589577534542408,
51.47239969138267
]
]
}
}
}
}
Now I have coordinates array-like [-0.0649729793707321, 51.50160291888072]
I need distance for each record from the above point. How can I find this distance field with MongoDB aggregation?
sample document for the database record
{
"_id" : ObjectId("5e342ac2e0dcfa65273751ea"),
"location" : {
"type" : "Point",
"coordinates" : [
-0.123507,
51.5083228
]
},
"status" : 1,
"price" : {
"value" : 27,
"type" : "day",
"base" : 3.38
},
"name" : "Initial Space with Wormup",
"venuePolicy" : "Venue Policies",
"updatedAt" : ISODate("2020-02-20T12:28:16.024Z"),
"createdAt" : ISODate("2020-01-31T13:25:22.600Z"),
"dimension" : {
"width" : 100
},
"description" : "<p>Hi, </p><p>please find attached document for testing purpose",
}
This one should work:
db.collection.aggregate([
{
"$match": {
"location": {
"$geoWithin": {
"$box": [
[0.024719919885622943, 51.54643953472475],
[-0.1589577534542408, 51.47239969138267]
]
}
}
}
},
{ $set: { lon: -0.0649729793707321 } },
{ $set: { lat: 51.50160291888072 } },
{
$set: {
distance: {
$let: {
vars: {
dlon: { $degreesToRadians: { $subtract: [{ $arrayElemAt: ["$location.coordinates", 0] }, "$lon"] } },
dlat: { $degreesToRadians: { $subtract: [{ $arrayElemAt: ["$location.coordinates", 1] }, "$lat"] } },
lat1: { $degreesToRadians: { $arrayElemAt: ["$location.coordinates", 1] } },
lat2: { $degreesToRadians: "$lat" }
},
in: {
// Haversine formula: sin²(dLat / 2) + sin²(dLon / 2) * cos(lat1) * cos(lat2);
$add: [
{ $pow: [{ $sin: { $divide: ["$$dlat", 2] } }, 2] },
{ $multiply: [{ $pow: [{ $sin: { $divide: ["$$dlon", 2] } }, 2] }, { $cos: "$$lat1" }, { $cos: "$$lat2" }] }
]
}
}
}
}
},
{
$set: {
distance: {
// Distance in Meters given by "6372.8 * 1000"
$multiply: [6372.8, 1000, 2, { $asin: { $sqrt: "$distance" } }]
}
}
},
])
Just a note, $box
is only used for Legacy Coordinate Pairs . For proper query (ie proper use of 2dsphere
index) you should use $geometry
:
db.collection.createIndex({ location: "2dsphere" })
db.collection.find(
{
"location": {
"$geoWithin": {
"$geometry": {
type: "Polygon",
coordinates: [[
[-0.1589577534542408, 51.47239969138267],
[-0.1589577534542408, 51.54643953472475],
[0.024719919885622943, 51.54643953472475],
[0.024719919885622943, 51.47239969138267],
[-0.1589577534542408, 51.47239969138267]
]]
}
}
}
}
).explain().queryPlanner.winningPlan.inputStage.indexName
--> location_2dsphere // which means index is used
db.collection.find(
{
"location": {
"$geoWithin": {
"$box": [
[0.024719919885622943, 51.54643953472475],
[-0.1589577534542408, 51.47239969138267]
]
}
}
}
).explain().queryPlanner.winningPlan.stage
--> COLLSCAN // which means full collection scan
Or proper use of Legacy Coordinates:
db.collection.createIndex({ "location.coordinates": "2d" })
db.collection.find(
{
"location.coordinates": { // <- note this difference
"$geoWithin": {
"$box": [
[0.024719919885622943, 51.54643953472475],
[-0.1589577534542408, 51.47239969138267]
]
}
}
}
).explain().queryPlanner.winningPlan.inputStage.indexName
--> location.coordinates_2d
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.