简体   繁体   中英

get the document with $box with mongodb and add distance field for each record with specific coordinate point

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,&nbsp;</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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM