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

Posted

技术标签:

【中文标题】在没有 [long,lat] 数组的情况下运行 mongodb $geoWithin【英文标题】:Running mongodb $geoWithin without a [long,lat] array 【发布时间】:2016-03-09 10:05:29 【问题描述】:

我有一个 mongodb $geoWithin 查询如下

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

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


   lat:12,
   long:122

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

【问题讨论】:

【参考方案1】:

您真正能做的最好的事情就是转换您的文档以更好地存储数据。根据偏好,您可能应该选择 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]]
                 
            
         
    
])

“永久”转换


但最好的情况是永久更改文档结构。做到这一点的现代方法是在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 查询,这也可以从当前的索引数据中更快地工作。

【讨论】:

试图在查询db.getCollection('test').aggregate([ // Tranform to array - pretty simple huh! "$project": "location": ["longitudeDegrees","latitudeDegrees"] , // Then match "$match": "location": "$geoWithin": "$geometry": "type": "Polygon" , "coordinates": [[[0,0], [0,6], [6,6], [6,0] ]] ]) 下运行并收到此错误异常:对象表达式中不允许的字段类型数组(在“位置”) @BiJ 仔细阅读。这仅对最新的 MongoDB 3.2 版本有效。您可能需要$map 版本,这就是为什么它在顶部。 @BiJ 你也错过了变量。如果您的字段实际上命名为"longitudeDegrees",那么您宁愿这样做: "$project": "location": [ "$longitudeDegrees","$latitudeDegrees"] 。但同样,这只适用于 MongoDB 3.2.x 版本。其他的发布方式都展示了。 对不起我的错误。我正在运行 mongo 3.0.5,所以我想这不适用于我。我尝试使用具有以下坐标 [0,0]、[0,6]、[6,6]、[6,0]、[0,0] 的 $map 运行查询。查询执行得很好,但我有一个集合 "_id" : ObjectId("56dffa8f010fc3915d4d8c82"), "latitudeDegrees" : 3, "longitudeDegrees" : 3 ,它没有显示在结果中。结果为空 我执行的查询是db.getCollection('test').aggregate(["$project":"location":"$map":"input":["longitudeDegrees","latitudeDegrees"],"as":"el","in":"$cond":["$eq":["$$el","longitudeDegrees"],"$long","$lat"],"$match":"location":"$geoWithin":"$geometry":"type":"Polygon","coordinates":[[[0,0],[0,6],[6,6],[6,0],[0,0]]]])

以上是关于在没有 [long,lat] 数组的情况下运行 mongodb $geoWithin的主要内容,如果未能解决你的问题,请参考以下文章

C# 使用 lat/long 数组计算多个曲率

Lat,Long 值不准确到 iphone 中的当前位置

将数组形式的字符串转换为实际的数组

为啥这个用于 lat/long 的 Lua Haversine 代码不起作用?

快速将 PFGeoPoint Lat 和 Long 从 Parse 转换为 CLLocation Lat 和 Long

计算街道的哈希值