MongoDB Geospacial Query Spheres Overlapping Single Point

老子叫甜甜 提交于 2019-12-03 07:44:36

Well it would be nicer if you could use a GeoJSON object to represent the location but as of present the supported types are actually limited so a "Circle" type which would be ideal is not supported.

The closest you could do is a "Polygon" approximating a circle, but this is probably a little too much work to construct just for this query purpose. The other gotcha with doing this and then applying $geoIntersects is that the results will not be "sorted" by the distance from the query point. That seems to be the opposite of the purpose of finding the "nearest pizza" to the point of origin.

Fortunately there is a $geoNear operation added to the aggregation framework as of MongoDB 2.4 and greater. The good thing here is it allows the "projection" of a distance field in the results. This then allows you to do the logical filtering on the server to those points that are "within the radius" constraint to the distance from the point of origin. It also allows sorting on the server as well.

But you are still going to need to change your schema to support the index

db.places.insert({
    "name": "Pizza Hut",
    "location": { 
        "type": "Point",
        "coordinates": [
            151.00211262702942,
            -33.81696995135973
        ]
    },
    "radius": 20
})

db.places.ensureIndex({ "location": "2dsphere" })

And for the aggregation query:

db.places.aggregate([

    // Query and project distance
    { "$geoNear": {
        "near": { 
            "type": "Point",
            "coordinates": [ 
                150.92094898223877,
                -33.77654333272719
            ]
        },
        "distanceField": "distance",
        "distanceMultiplier": 0.001,
        "maxDistance": 100000,
        "spherical": true
    }},

    // Calculate if distance is within delivery sphere
    { "$project": {
         "name": 1,
         "location": 1,
         "radius": 1,
         "distance": 1,
         "within": { "$gt": [ "$radius", "$distance" ] }
    }},

    // Filter any false results
    { "$match": { "within": true } },

    // Sort by shortest distance from origin
    { "$sort": { "distance": -1 } }
])

Basically this says,

*"out to 100 kilometers from a given location, find the places with their distance from that point. If the distance is within their "delivery radius" then return them, sorted by the closest"

There are other options you can pass to $geoNear in order to refine the result, as well as return more than the default 100 results if required and basically pass other options to query such as a "type" or "name" or whatever other information you have on the document.

Correction: MongoDB (as of v3.2.0), does not support the Circle GeoJSON type for 2dsphere indexes.

What follows is the original and incorrect answer. Read the comments for details.


I don't think that using the defined schema you'd be able to work out what you're looking for.

With mongodb >= 2.4 you could store and index documents using GeoJSON, and then use the $geoIntersects command, like so:

db.places.insert({
    name: "Pizza Hut",
    location: { 
        "type": "Circle",
        "coordinates": [100,0],
        "radius": 20
    }
})


db.places.find({
    location: {
        $geoIntersects: {
            $geometry: {
                type: "Point",
                coordinates: [ 110, 10]
            }
        }
    }
})

Otherwise, then a combination of $geoWithin and manual processing application-side might work, although I'd rather redefine my schema, if possible.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!