MongoDB 不能像您想要的那样“分解” GeoJSON 对象以匹配“最近点”。因此,您的问题可能更适合另一种结构,该结构将允许独立考虑每个“停止”,而不是仅将其表示为“LineString”上的一个点。
在查询中使用它的一种非常有效的方法是:
{
"routename": "A123",
"stops": [
{
"type": "out",
"location": {
"type": "Point",
"coordinates": [100.0,0.0]
}
},
{
"type": "out",
"location": {
"type": "Point",
"coordinates": [101.0,1.0]
}
},
{
"type": "return",
"location": {
"type": "Point",
"coordinates": [102.0,2.0]
}
},
{
"type": "return",
"location": {
"type": "Point",
"coordinates": [103.0,3.0]
}
}
]
}
这将路线上的每个“停靠点”分解到一个不同的“点”,并保持路线“类型”表示停靠点是在行程的“离开”或“返回”路段。
然后您可以使用$geoNear 运行此聚合查询,以便匹配和投影数组条目中的“最近点”。下一阶段将匹配的位置与数组中的每个“停靠点”进行比较,以提取该停靠点所属的路线段:
db.routes.aggregate([
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [103.0, 2.0]
},
"distanceField": "distance",
"spherical": true,
"includeLocs": "loc"
}},
{ "$project": {
"routeName": 1,
"distance": 1,
"route": {
"$let": {
"vars": {
"matchedStop": {
"$setDifference": [
{ "$map": {
"input": "$stops",
"as": "stop",
"in": {
"$cond": [
{ "$eq": [ "$$stop.location", "$loc" ] },
"$$stop",
false
]
}
}},
[false]
]
}
},
"in": {
"$setDifference": [
{ "$map": {
"input": "$stops",
"as": "stop",
"in": {
"$cond": [
{ "$setEquals": [
{ "$map": { "input": ["A"], "as": "el", "in": "$$stop.type" } },
"$$matchedStop.type"
]},
{
"type": "$$stop.type",
"location": "$$stop.location",
"nearest": {
"$eq": [ "$$stop.location", "$loc" ]
}
},
false
]
}
}},
[false]
]
}
}
}
}}
])
这会给你这种类型的输出:
{
"_id" : ObjectId("55ee6c0a2343a0d2e1650e28"),
"distance" : 111251.03086891436,
"route" : [
{
"type" : "return",
"location" : {
"type" : "Point",
"coordinates" : [
102,
2
]
},
"nearest" : true
},
{
"type" : "return",
"location" : {
"type" : "Point",
"coordinates" : [
103,
3
]
},
"nearest" : false
}
]
}
这主要是大量使用$map 来提取所需的元素。所以第一种情况是从$geoNear中提取“includeLocs”返回中已经识别的匹配元素。然后,这允许您重新处理数组并仅过滤掉包含相同“类型”的“元素”用于路线的腿,并且为了增加美观性,我们还标记了数组成员实际上是“最近”匹配的时间。
特别是与要匹配的路由“类型”,早先$map返回的变量实际上是一个数组。所以你只能真正将一个数组与另一个“数组”进行比较,这就是为什么$map 再次用于单个字段值,以便将其变成一个数组本身:
{ "$setEquals": [
{ "$map": { "input": ["A"], "as": "el", "in": "$$stop.type" } },
"$$matchedStop.type"
]},
这就是该部分的全部内容,当然$setEquals 会测试两个数组以查看它们是否实际上相同。
这里的其他内容是$let,以便让“matchedStop”更易于访问,$setDifference 实质上从通过对数组成员进行的测试中过滤掉false 的任何返回值$cond 运营商。
当然,这里的替代方案基本上只是将每个“站点”分离到集合中它自己的文档中,然后运行一个查询以查找“最近”文档,并运行另一个查询以查找相同的“路线”和其他在您的返回信息中停在该路线的同一“腿”上。但这仍然会被包含在一个文档和匹配中,并且非常有效,尤其是对于多个“路由”匹配。