在 MongoDB 中,如何使用文档中的字段作为 $geoWithin/$centerSphere 表达式的输入?
In MongoDB, how do I use a field in the document as input to a $geoWithin/$centerSphere expression?
我正在尝试编写一个 MongoDB 查询来搜索以指定位置为中心的半径范围内的文档。
下面的查询有效。它会找到 searching.coordinates
的 searching.radius
弧度范围内的所有文档。
但是我想做的是将当前文档 allowed_radius
值添加到 searching.radius
值,这样允许的范围实际上更大。
我该如何表达这个查询才能实现这一点?
当前查询:
collection.aggregate([
{
$project:{
location: "$location",
allowed_radius: "$allowed_radius"
}
},
{
$match: {
$and:
[
{ location: { $geoWithin: { $centerSphere: [ searching.coordinates, searching.radius ] }}},
{...},
...]
...}
]);
我要做什么(伪查询):
collection.aggregate([
{
$project:{
location: "$location",
allowed_radius: "$allowed_radius"
}
},
{
$match: {
$and:
[
{ location: { $geoWithin: { $centerSphere: [ searching.coordinates, { $add: [searching.radius, $allowed_radius]} ] }}},
{...},
...]
...}
]);
我试过使用 $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
})
这个索引($geoNear 需要):
db.collection.createIndex( { location : "2dsphere" } )
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 } } }
])
将return id 为 1 (distance=1002 <= radius=1000+100) 和 3 (distance=676 <= radius=1000+10) 的文档并丢弃 id 2 (distance=1002 > 1000+0).
distanceMultiplier
参数用于将单位恢复为千米。
$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 }
事实上,geoNear 运算符需要使用 distanceField
参数,该参数用于在查询的下一阶段将计算的距离投影到每条记录上。在聚合结束时,returned 记录如下所示:
{
"airport" : "ORY",
"location" : { "type" : "Point", "coordinates" : [ 2.35944, 48.72528 ] },
"allowed_radius" : 10,
"id" : 3,
"distance" : 676.5790971238937,
"radius" : 1010,
"isIn" : -333.4209028761063
}
如有必要,您可以删除由最终 $project 阶段的查询 (distance, radius, isIn) 生成的字段。例如:{"$project":{"distance":0}}
我正在尝试编写一个 MongoDB 查询来搜索以指定位置为中心的半径范围内的文档。
下面的查询有效。它会找到 searching.coordinates
的 searching.radius
弧度范围内的所有文档。
但是我想做的是将当前文档 allowed_radius
值添加到 searching.radius
值,这样允许的范围实际上更大。
我该如何表达这个查询才能实现这一点?
当前查询:
collection.aggregate([
{
$project:{
location: "$location",
allowed_radius: "$allowed_radius"
}
},
{
$match: {
$and:
[
{ location: { $geoWithin: { $centerSphere: [ searching.coordinates, searching.radius ] }}},
{...},
...]
...}
]);
我要做什么(伪查询):
collection.aggregate([
{
$project:{
location: "$location",
allowed_radius: "$allowed_radius"
}
},
{
$match: {
$and:
[
{ location: { $geoWithin: { $centerSphere: [ searching.coordinates, { $add: [searching.radius, $allowed_radius]} ] }}},
{...},
...]
...}
]);
我试过使用 $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
})
这个索引($geoNear 需要):
db.collection.createIndex( { location : "2dsphere" } )
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 } } }
])
将return id 为 1 (distance=1002 <= radius=1000+100) 和 3 (distance=676 <= radius=1000+10) 的文档并丢弃 id 2 (distance=1002 > 1000+0).
distanceMultiplier
参数用于将单位恢复为千米。
$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 }
事实上,geoNear 运算符需要使用 distanceField
参数,该参数用于在查询的下一阶段将计算的距离投影到每条记录上。在聚合结束时,returned 记录如下所示:
{
"airport" : "ORY",
"location" : { "type" : "Point", "coordinates" : [ 2.35944, 48.72528 ] },
"allowed_radius" : 10,
"id" : 3,
"distance" : 676.5790971238937,
"radius" : 1010,
"isIn" : -333.4209028761063
}
如有必要,您可以删除由最终 $project 阶段的查询 (distance, radius, isIn) 生成的字段。例如:{"$project":{"distance":0}}