How to define a circle for a mongo db schema?

前端 未结 1 1323
一向
一向 2021-01-26 03:25

I have the folowing code to define a schema:

var mongoose=require(\'mongoose\');
var Schema=mongoose.Schema;
var PostSchema=new Schema({
location:{type:Array,req         


        
相关标签:
1条回答
  • 2021-01-26 03:40

    To be valid for a "geospatial query" the "location" must be in longitude, latitude order and cannot contain any other coordinates.

    Valid formats are

     { 
         "location": [long,lat]
     }
    

    Or

     {
        "location": { "lng": long, "lat": lat }
     }
    

    Or GeoJSON

     {
         "location": {
             "type": "Point",
             "coordinates": [long,lat]
         }
     }
    

    Another field such as "radius" is "another field" and cannot be part of the same array.

    Ideally follow GeoJSON:

     {
         "location": {
             "type": "Point",
             "coordinates": [long,lat]
         },
         "radius": radius
     }
    

    Which in mongoose schema definition can be as simple as:

    var geoSchema = new Schema({
        "location": {
            "type": String,
            "coordinates": []
        },
        "radius": Number
    });
    

    When dealing with geospatial data at real "globe" coordinates your index should be "2dsphere", which you optionally define on the schema as :

    geoSchema.index({ "location": "2dsphere" })
    

    Since there is no actual support for a "Circle" object in supported GeoJSON then keeping another field as "radius" and storing the "center point" is recommended.

    The "big" advantage with GeoJSON over the other "legacy coordinate pairs" formats is that when returning something like a "distance" from a point via geoNear or $geoNear then that "distance" is defined in "meters" consistently. This is also how you should be defining any "radius" value in your storage to remain consistent with that result.

    With the other storage formats then the result is returned in "radians", for which you probably want to convert and would prefer not to be storing a "radius" of a circle with that as a measurement.

    The way you deal with this is, considering data in this form:

    {
        "locationtype": "circle",
        "location": {
            "type": "Point",
            "coordinates": [1,1]
        },
        "radius": 4
    }
    

    Then you use .aggregate() with a $geoNear stage and a $redact to filter:

    db.collection.aggregate([
        // Find points or objects "near" and project the distance
        { "$geoNear": {
            "near": {
                "type": "Point",
                "coordinates": [2,2]
            },
            "distanceField": "distance",
            "query": { "locationType": "circle" }
        }},
        // Logically filter anything outside of the radius
        { "$redact": {
            "$cond": {
                "if": { "$gt": [ "$distance", "$radius" ] },
                "then": "$$PRUNE",
                "else": "$$KEEP"
            }
        }}
    ])
    

    Now the values used in the query example are just an example, but as stated with "real" longitude and latitude coordinates the "distance" attributes work out as designed and within the "meters" tolerance as mentioned earlier.

    The points here are that $geoNear will both find "near" to the "circle" center poiny no matter what the object type. Not only that but the command here is producing a "projection" of another field in the document here as named in "distanceField". This represents the distance from the circle "center" in "meters".

    The second stage here uses $redact since it is sort of like a $project and $match pipeline stage in one. Unlike $match this operator can evaluate a "logical" condition by comparing fields present in the document. In this case, operations like $$PRUNE remove the matched document to the "if" condition where true and "remove" it from results or otherwise $$KEEP the document where the condition was false.

    In a "nutshell", if "distance" is "greater than" then "radius" of the "circle" then the object "lies outside" of the circle and does not "intersect". Otherwise "it does".


    So that is the basics of "defining a 'circle' for geometry in a collection and "using it" to achieve something like the intersection between a "Point" or other type of Object within the "circle" radius.

    0 讨论(0)
提交回复
热议问题