假设您已经着手处理收到的事件数据并将其放在手中(如果您还没有,那么这是另一个问题,但请查看tailable cursors),那么您应该有一个对象用于查询用户的数据。
因此,这不适用于使用 $where 进行 JavaScript 评估,因为无论如何它都无法访问从 $near 操作返回的查询数据。你想要的是来自聚合框架的$geoNear。这可以投影从查询中找到的“距离”,并允许稍后阶段根据用户存储的值“过滤”结果,以获得他们想要前往已发布事件的最大距离:
// Represent retrieved event data
var eventData = {
eventLocation: {
latlong: [long,lat]
}
};
// Find users near that event within their stored distance
User.aggregate(
[
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": eventData.eventLocation.latlong
},
"distanceField": "eventDistance",
"limit": 100000,
"spherical": true
}},
{ "$redact": {
"$cond": {
"if": { "$lt": [ "$eventDistance", "$maxDistance" ] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
]
function(err,results) {
// Work with results in here
}
)
现在您确实需要小心返回的数字,因为您似乎存储在“旧坐标对”而不是 GeoJSON 中,因此从该操作返回的距离将以弧度表示,而不是标准距离。因此,假设您在用户对象上存储为“英里”或“公里”,那么您需要通过手册中提到的公式在"Calculate Distances Using Spherical Geometry" 下进行计算,如手册中所述。
基本原理是您需要除以地球的赤道半径,即 3,963.2 英里或 6,378.1 公里,以便与您存储的数据进行比较。
替代方法是存储在 GeoJSON 中,其中以米为单位进行一致的测量。
假设“如果”行变成“公里”:
"if": { "$lt": [
"$eventDistance",
{ "$divide": [ "$maxDistance", 6,378.1 ] }
]},
可靠地将您存储的公里值与返回的弧度结果进行比较。
要注意的另一件事是$geoNear 的默认“限制”为 100 个结果,因此您需要将“限制”参数“提高”到预期用户可能匹配的数量。对于一个非常大的系统,您甚至可能希望在用户 ID 的“范围列表”中执行此操作,但您可以在单个聚合操作中尽可能大地使用内存,并可能在需要时添加 allowDiskUse。
如果您不调整该参数,则只会返回最接近的 100 个结果(默认值),这甚至可能不适合您过滤那些“接近”事件开始的下一个操作。不过请使用常识,因为您肯定有一个最大距离,甚至可以过滤掉潜在用户,并且也可以将其添加到查询中。
如上所述,这里的重点是返回距离进行比较,因此下一阶段是$redact 操作,它可以将用户自己的“旅行距离”值与返回的事件距离相匹配。最终结果只为那些在他们自己的距离范围内的用户提供了有资格获得通知的事件。
这就是逻辑。您预测从用户到事件的距离,然后与用户存储的值进行比较,以了解他们准备行进的距离。没有 JavaScript,所有的原生运算符都让它变得非常快。
也如选项和一般评论中所述,我确实建议您使用“2dsphere”索引进行准确的球面距离计算,以及转换为 GeoJSON 存储以存储数据库对象中的坐标,因为它们都是产生一致结果的通用标准。