使用不带点的地理空间查询获取一定距离内的文档 object

Get documents within a distance using Geospatial Query without Point object

我有一个地方 collection 将位置清楚地存储为

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 ]
    } 
   }
})

或者我必须将 collection 修改为

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

你真的应该改变你的数据。 MongoDB 仅支持用于地理空间索引和查询的旧坐标对格式或 GeoJSON。您不能以任何方式对数据或 "transform" 使用不同的字段,因为使用 $near or $nearSphere.

进行操作所必需的 "index" 需要支持的字段格式

最好在 shell 中进行转换,因为不需要为 "one off" 操作编写其他 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]
        }
    }
})

我还应该注意到,$geoWithin 中的 $centreSphere 操作实际上与带有 $maxDistance 修饰符的 $nearSphere 相同。例外情况是后者应该同时处理 "faster" 并为 "nearest" 位置生成 "ordered" 结果,这是 $geoWithin 不做的事情:

db.places.find({
    "$nearSphere": {
        "$geometry": {
            "type": "Point",
            "coordinates": [x,y]
        },
        "$maxDistance": z
    }
})

对现有数据执行此操作的唯一方法是仅针对 $geoWithin。这是因为该操作 需要地理空间索引,因此您可以先 "transform" 文档。

您可以使用 .aggregate() method and it's $project pipeline stage along with the $map 运算符执行此操作:

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]
            }
        }
    }}
])

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

所以可以,但不可取。它会增加处理时间,如果您修复数据并添加适当的索引,事情会变得更好、更灵活和 "faster"。

另请注意,使用 GeoJSON 数据的所有距离都将以千米而不是弧度为单位。