使用没有Point对象的地理空间查询在一定距离内获取文档

时间:2015-08-08 07:12:53

标签: mongodb mongodb-query geospatial aggregation-framework

我有一个地方集合,明确地将位置存储为

place = {
   name : "",
   latitude: "",
   longitude:""
}

有没有办法使用mongo shell或spring数据mongo我可以查询这样的地方:

select all places with coordinates(places.longitude, place.latitude) near a point(x,y) and within a distance z . Something like: 

db.places.find( { 
   {
    "type" : "Point",
    "coordinates" : [ 
        places.longitude, 
        places.latitude
    ]
   }: 
   { $geoWithin: 
    { $centerSphere: [ [ x, y ] ,z / 3963.2 ]
    } 
   }
})

或者我必须将我的收藏修改为

place = {
   name : "",
   "loc" : {
       "type" : "Point",
       "coordinates" : [ 
          longitude, 
          latitude
       ]
   }
}

1 个答案:

答案 0 :(得分:1)

你真的应该改变你的数据。 MongoDB仅支持传统坐标对格式或GeoJSON,仅支持地理索引和查询。您不能以任何方式对数据使用不同的字段或“转换”,因为使用$near$nearSphere的操作所需的“索引”需要支持的字段格式。

最好转换到shell中的转换,因为为“一次性”操作编写其他API代码是不必要的。是的,你真的应该使用GeoJSON格式:

var bulk = db.places.initializeUnorderedBulkOp(),
    count = 0;

db.places.find().forEach(function(doc) {
   bulk.find({ "_id": doc._id }).updateOne({
       "$set": {
           "location": {
               "type": "Point",
               "coordinates": [parseFloat(doc.longitude),parseFloat(doc.latitude)]
           }        
       },
       "$unset": { "latitude": "", "longitude": "" }
   });
   count++;

   if ( count % 1000 == 0 ) {
       bulk.execute();
       bulk = db.places.initializeUnorderedBulkOp();
   }
});

if ( count % 1000 !=0 )
   bulk.execute();

现在数据已修复并与索引兼容,创建索引。这里使用GeoJSON数据有意义的是"2sphere"索引:

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

之后,您可以正常查询文档:

db.places.find({
    "location": { 
        "$geoWithin": {
            "$centerSphere": [ [ x, y ] ,z]
        }
    }
})

我还应该注意,$centreSphere中的$geoWithin操作实际上与$nearSphere修饰符的$geoWithin操作相同。唯一的例外是后者应该“更快”处理以及为“最近”的位置生成“有序”结果,这是db.places.find({ "$nearSphere": { "$geometry": { "type": "Point", "coordinates": [x,y] }, "$maxDistance": z } }) 不能做的事情:

db.places.aggregate([
    { "$project": {
        "name": 1
        "location": {
            "type": "Point",
            "coordinates": {
                "$map": {
                    "input": ["A","B"],
                    "as": "el",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$el", "A" ] },
                            "$longitude",
                            "$latitude"
                        ]
                    }
                }
            }
        }
    }},
    { "$match": {
        "location": { 
            "$geoWithin": {
                "$centerSphere": [ [ x, y ] ,z]
            }
        }
    }}
])

对现有数据执行此操作的唯一方法是仅$maxDistance。这是因为该操作需要地理空间索引,因此您可以首先“转换”文档。

您可以使用$geoWithin方法和.aggregate()管道阶段以及$project运算符执行此操作:

$nearSphere

但是您的经度和纬度数据必须已经是数字,因为这是您无法在聚合框架中转换的内容。并且您必须记住,不能用于customer之类的操作,因为在初始管道阶段之后所需的索引不可用。

所以可以做到,但这是不可取的。这将增加处理时间,如果您修复数据并添加适当的索引,事情会变得更好,更灵活,更“快”。

另请注意,GeoJSON数据的所有距离均以公里为单位,而不是弧度。