在MongoDB中,如何使用文档中的字段作为$ geoWithin/$ centerSphere表达式的输入?

Cod*_*ein 7 geospatial mongodb mongodb-query aggregation-framework

我正在尝试编写一个MongoDB查询,用于搜索以指定位置为中心的半径内的文档.

以下查询有效.它找到所有在searching.radius弧度范围内的文档searching.coordinates.

但是我想要做的是将当前文档allowed_radius值添加到searching.radius值中,以便允许的球体实际上更大.

如何使用此查询来实现此功能?

现在的查询:

collection.aggregate([
        {
            $project:{
                location: "$location",
                allowed_radius: "$allowed_radius"
            }
        },
        {
            $match: {
                $and:
                    [
                        { location: { $geoWithin: { $centerSphere: [ searching.coordinates, searching.radius ] }}},
                        {...},
                    ...]
                ...}
]);
Run Code Online (Sandbox Code Playgroud)

我想做什么(伪查询):

collection.aggregate([
        {
            $project:{
                location: "$location",
                allowed_radius: "$allowed_radius"
            }
        },
        {
            $match: {
                $and:
                    [
                        { location: { $geoWithin: { $centerSphere: [ searching.coordinates, { $add: [searching.radius, $allowed_radius]} ] }}},
                        {...},
                    ...]
                ...}
]);
Run Code Online (Sandbox Code Playgroud)

Xav*_*hot 5

我尝试过使用$geoWithin / $centerSphere,但无法以这种方式工作.

这是使用$ geoNear运算符的另一种方法:

鉴于此输入:

db.collection.insert({
  "airport": "LGW",
  "id": 1,
  "location": { type: "Point", coordinates: [-0.17818, 51.15609] },
  "allowed_radius": 100
})
db.collection.insert({
  "airport": "LGW",
  "id": 2,
  "location": { type: "Point", coordinates: [-0.17818, 51.15609] },
  "allowed_radius": 0
})
db.collection.insert({
  "airport": "ORY",
  "id": 3,
  "location": { type: "Point", coordinates: [2.35944, 48.72528] },
  "allowed_radius": 10
})
Run Code Online (Sandbox Code Playgroud)

而这个索引(这是$ geoNear所需):

db.collection.createIndex( { location : "2dsphere" } )
Run Code Online (Sandbox Code Playgroud)

使用searching.radius = 1000:

db.collection.aggregate([
  { $geoNear: {
    near: { "type" : "Point", "coordinates":  [7.215872, 43.658411] },
    distanceField: "distance",
    spherical: true,
    distanceMultiplier: 0.001
  }},
  { $addFields: { radius: { "$add": ["$allowed_radius", 1000] } } },
  { $addFields: { isIn: { "$subtract": ["$distance", "$radius" ] } } },
  { $match: { isIn: { "$lte": 0 } } }
])
Run Code Online (Sandbox Code Playgroud)

将返回id为1(距离= 1002 <=半径= 1000 + 100)和3(距离= 676 <=半径= 1000 + 10)并丢弃id 2(距离= 1002> 1000 + 0)的文档.

distanceMultiplier参数用于将单位恢复为km.

$ geoNear必须是聚合的第一阶段(由于我认为使用了索引),但$ geoNear的一个参数是其他字段的匹配查询.

即使它需要地理位置索引,您也可以为索引添加其他维度.

$ geoNear不将位置字段作为参数,因为它要求集合具有地理索引.因此,$ geoNear隐式地使用索引的位置字段(无论字段的名称).

最后,我很确定最后阶段可以简化.

$ geoNear阶段仅用于投影每条记录的距离:

{ "airport" : "ORY", "distance" : 676.5790971238937, "location" : { "type" : "Point", "coordinates" : [ 2.35944, 48.72528 ] }, "allowed_radius" : 10, "id" : 3 }
{ "airport" : "LGW", "distance" : 1002.3351814526812, "location" : { "type" : "Point", "coordinates" : [ -0.17818, 51.15609 ] }, "allowed_radius" : 100, "id" : 1 }
{ "airport" : "LGW", "distance" : 1002.3351814526812, "location" : { "type" : "Point", "coordinates" : [ -0.17818, 51.15609 ] }, "allowed_radius" : 0, "id" : 2 }
Run Code Online (Sandbox Code Playgroud)

事实上,geoNear运算符需要使用distanceField参数,该参数用于在查询的下一个阶段为每个记录投影计算的距离.在聚合结束时,返回的记录如下所示:

{
  "airport" : "ORY",
  "location" : { "type" : "Point", "coordinates" : [ 2.35944, 48.72528 ] },
  "allowed_radius" : 10,
  "id" : 3,
  "distance" : 676.5790971238937,
  "radius" : 1010,
  "isIn" : -333.4209028761063
}
Run Code Online (Sandbox Code Playgroud)

如有必要,您可以删除查询生成的字段(距离,半径,isIn)和最终的$ project阶段.例如:{"$project":{"distance":0}}