我有一个看起来像这样的集合:
{
"_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
个索引
我使用aggreagate
与geonear
查询这些文件:
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要求太多了?
答案 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提供的距离数据似乎只涉及单个文档,因此如果您想要按多个文档进行过滤,将来可能会出现问题。