在没有[long,lat]数组的情况下运行mongodb $ geoWithin

时间:2016-03-09 10:05:30

标签: mongodb geolocation mongodb-query aggregation-framework

我有一个mongodb $geoWithin查询,如下所示

db.test.find(
         {
          'loc': {
             $geoWithin: {
                 $geometry: {
                        type : "Polygon" ,
                        coordinates: [[list of co-ordinates]]
                  }
              }
            }
          }
 );

所以此处查询在loc字段上运行,该字段是lnglat值的数组,但幸运的是,我的数据latlng值在2个不同的字段中,如

{
   lat:12,
   long:122
}

在这种情况下,我该如何运行上述查询?

1 个答案:

答案 0 :(得分:4)

您可以做的最好的事情是转换文档以更好地存储数据。您可能应该选择GeoJSON格式。但更晚些时候。

幸运的是,$geoWithin实际上并不需要"一个索引(但它确实是更好的选择)你可以实际进行转换"在飞行中"而是使用聚合框架:

转变"在飞行中"

希望你至少拥有 MongoDB 2.6 ,有$map

db.collection.aggregate([

    // Tranform to array
    { "$project": {
        "location": {
            "$map": {
                "input": ["lng","lat"],
                "as": "el",
                "in": {
                    "$cond": [
                        { "$eq": [ "$$el", "lng" ] },
                        "$long",
                        "$lat"
                    }
                }
            }
        }
    }},

    // Then match
    { "$match": {
        "location": {
            "$geoWithin": {
                "$geometry": {
                    "type": "Polygon" ,
                    "coordinates": [[list of co-ordinates]]
                 }
            }
        } 
    }}
])

MongoDB 3.2 有一个更简单的语法:

db.collection.aggregate([

    // Tranform to array - pretty simple huh!
    { "$project": {
        "location": ["$long","$lat"]
    }},

    // Then match
    { "$match": {
        "location": {
            "$geoWithin": {
                "$geometry": {
                    "type": "Polygon" ,
                    "coordinates": [[list of co-ordinates]]
                 }
            }
        } 
    }}
])

或者如果你还有 MongoDB 2.4 - 升级!好的,然后使用它:

db.collection.aggregate([

    // Add an array field
    { "$project": {
        "long": 1,
        "lat": 1
        "location": { "$const": [ "A", "B" ] }
    }},

    // Unwind it
    { "$unwind": "$location" },

    // Group back and map it!
    { "$group": {
        "_id": "$_id",
        "location": {
            "$push": {
                "$cond": [
                    { "$eq": [ "$location", "A" ] },
                    "$long",
                    "$lat"
                ]
            }
        }
     }},

    // Then match
    { "$match": {
        "location": {
            "$geoWithin": {
                "$geometry": {
                    "type": "Polygon" ,
                    "coordinates": [[list of co-ordinates]]
                 }
            }
        } 
    }}
])

Tranform"永久"

但实际上最好的情况是永久改变文档结构。现代的方法是bulk,例如:

var ops = [];

db.collection.find({}).forEach(function(doc) {

    ops.push({
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": {
                "$set": { 
                    "location": {
                        "type": "Point",
                        "coordinates": [doc.long,doc.lat]
                    }
                },
                "$unset": { "long": "", "lat": "" }
            }
        }
    });

    // Send once in 1000 only
    if ( ops.length % 1000 == 0 ) {
        db.collection.bulkWrite(ops);
        ops = [];
    }
})

// Clear remaining queue
if ( ops.length > 0 )
    db.collection.bulkWrite(ops);

但一般来说,循环源文档并更新每个文档以创建新的"位置"领域。然后当然"索引"它:

db.collection.createIndex({ "location": "2dsphere" })

现在文档看起来像这样并且实际上有一个索引,您可以直接使用常规$geoWithin查询,这也可以从当前索引数据中更快地运行。