MongoDB - $ geoNear在同一文档中有多个匹配

时间:2016-07-12 22:41:46

标签: mongodb

我有一个看起来像这样的集合:

{
    "_id" : ObjectId("5783051796a90cd8098fc3e0"),
    "name" : "Org 1",
    "locations" : [ 
        {
            "name" : "loc 1",
            "_id" : ObjectId("57831ac2febcceaf173e81ab"),
            "address" : {
                "coordinates" : [ 
                    172.6034932, 
                    -43.5333797
                ]
            }
        }, 
        {
            "name" : "loc 2",
            "_id" : ObjectId("5783436dc57b8d6248c9c196"),
            "address" : {
                "coordinates" : [ 
                    172.6034977, 
                    -43.5335158
                ]
            }
        }, 
        {
            "name" : "loc 3",
            "_id" : ObjectId("5783439bc57b8d6248c9c197"),
            "address" : {
                "coordinates" : [ 
                    168.6626435, 
                    -45.0311622
                ]
            }
        }
    ]
}

2dsphere

上有locations.address.coordinates个索引

我使用aggreagategeonear查询这些文件:

db.organisations.aggregate([
    {
        $geoNear: {
            near: { type: "Point", coordinates: [ 172.6036799 , -43.5335639 ] },
            distanceField: "dist.calculated",
            maxDistance: 5,
            includeLocs: "dist.location",
            num: 5,
            limit: 200,
            spherical: true
        }
    }
])

此聚合工作正常,我获得所选区域中至少有1个位置的所有文档。 问题是,当1个文档中有超过1个匹配时,我只想检索相关位置。

MongoDB可以告诉我哪一个最接近includeLocs选项,但这还不够,因为我想了解所有细节。

至少,如果我有每个位置的距离,我可以稍后在聚合管道上过滤它。使用distanceField选项,我可以获得整个文档的距离,但不能找到每个匹配的位置

回顾一下:我的目标是找到至少有一个匹配位置的所有文档,并知道这些位置数组中的相关位置。

有没有办法得到它,还是我对Mongo要求太多了?

1 个答案:

答案 0 :(得分:2)

鉴于澄清,完全重写了答案。

你是对的,$geoNear必须是第一阶段。解决方案在于使用“$ unwind”和“$ redact”将geoNear提供的匹配位置文档与相应的数组元素进行比较。

测试数据:

db.test.insert({ "locs" : [ 
{ "name" : "a", "address" : { "type" : "Point", "coordinates" : [ 0, 0 ] } }, 
{ "name" : "b", "address" : { "type" : "Point", "coordinates" : [ 1, 1 ] } }, 
{ "name" : "c", "address" : { "type" : "Point", "coordinates" : [ 2, 2 ] } }
]})

db.test.insert({ "locs" : [ 
{ "name" : "h", "address" : { "type" : "Point", "coordinates" : [ 1.01, 1.01 ] } }
]})

db.test.ensureIndex( { "locs.address" : "2dsphere" } )

查询:

db.test.aggregate([
{ "$geoNear" : { near : { "type" : "Point", "coordinates" : [ 1, 1 ] }, distanceField: "dist.calculated", maxDistance: 5000, includeLocs: "dist.location", num: 5, limit: 200, spherical: true } },
{ "$unwind" : "$locs" },
{ "$redact" : { 
  "$cond" : { 
    if : { "$eq" : [ { "$cmp" : [ "$locs.address", "$dist.location" ] }, 0 ] },
    then : "$$KEEP", 
    else : "$$PRUNE"
   } 
 } 
}
])

geoNear阶段将输出整个文档,其中“dist”字段显示距离和匹配位置字段:

{ 
  "_id" : ObjectId("5786fa0ddeb382a191a43122"), 
  "locs" : [ { "name" : "h", "address" : { "type" : "Point", "coordinates" : [ 1.01, 1.01 ] } } ],
  "dist" : { 
    "calculated" : 0, 
    "location" : { "type" : "Point", "coordinates" : [ 1, 1 ] }
  } 
}

我们$unwind“locs”数组允许访问各个数组元素。 dist字段被保留。

然后可以使用$redact字段删除地址与$ geoNear阶段返回的位置不匹配的任何数组元素。

结果:

{ 
 "_id" : ObjectId("5786fa0ddeb382a191a43121"), 
 "locs" : { "name" : "b", "address" : { "type" : "Point", "coordinates" : [ 1, 1 ] } }, 
 "dist" : { "calculated" : 0, "location" : { "type" : "Point", "coordinates" : [ 1, 1 ] } } 
}
{ 
 "_id" : ObjectId("5786fa0ddeb382a191a43122"), 
 "locs" : { "name" : "h", "address" : { "type" : "Point", "coordinates" : [ 1.01, 1.01 ] } }, 
 "dist" : { "calculated" : 1574.1651198970692, "location" : { "type" : "Point", "coordinates" : [ 1.01, 1.01 ] } } 
}

更广泛地说,您可能需要考虑调整数据模型,以便每个文档只包含一个地理空间点。 geoNear提供的距离数据似乎只涉及单个文档,因此如果您想要按多个文档进行过滤,将来可能会出现问题。