mongodb - 在聚合管道阶段以公里为单位计算点到点的距离
问题描述
此文档由聚合管道阶段返回:
//1
{
"_id" : {
"h" : 8,
"d" : 6,
"m" : 3,
"y" : 2017
},
"docs" : [ ....],
"firstDoc" : {
"ts" : ISODate("2017-03-06T08:07:21.008Z"),
"coordinate" : [
59.5......,
36.2......
]
}
} ,
//2
{
} , // 3 , 4
我有 4 个像上面这样的文件,这些firstDoc
字段是不同的。现在我想以千米为单位计算点之间的距离。像这样:
coordinate1---km?---coordinate2
coordinate2---km?---coordinate3
coordinate3---km?---coordinate4
我看到这个链接,但我不使用GeoJSON
,我不想要proximity
。我想计算点之间的距离我怎么能这样做?我搜索了很多,但在 mongoDB 中找不到任何结果!
我看到有人用其他语言(如java、c# 和java Script )来做到这一点,但没有人不使用 mongo 查询。mongoDB可以吗?
解决方案
好吧,它实际上已在第一个链接中涵盖,因为该"distanceMultipler"
字段可用于在数据未存储在 GeoJSON 中并使用任何可接受格式的旧坐标对时返回的“弧度”之间进行转换。
从该选项的$geoNear
文档中"distanceField"
:
可选的。乘以查询返回的所有距离的因子。例如,使用 distanceMultiplier 通过乘以地球的半径将球形查询返回的弧度转换为千米。
- 距离到弧度:用与距离测量相同的单位将距离除以球体(例如地球)的半径。
- 弧度到距离:将弧度测量值乘以要转换为距离的单位系统中的球体(例如地球)的半径。
地球的赤道半径约为 3,963.2 英里或 6,378.1 公里。
因此,您只需填写选项:
db.collection.aggregate([
{ "$geoNear": {
"near": [<longitude>,<latitude>],
"distanceField": "distance",
"spherical": true,
"distanceMultiplier": 6378.1 // convert radians to kilometers
}}
])
或者,如果您真的仔细阅读了文档,您会看到提供给 的条件有一个特殊情况"near"
,通过以下"spherical"
选项:
如果为 true,则如果指定(近)点是 GeoJSON 点,则 MongoDB 使用球面几何计算距离,如果指定(近)点是旧坐标对,则使用弧度计算距离。
因此,简单地提供 GeoJSON 格式的查询坐标返回以米为单位,而不管存储格式如何:
db.collection.aggregate([
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [<longitude>,<latitude>]
},
"distanceField": "distance",
"spherical": true
}}
])
另请注意,与其他一些“查询”运算符不同,$geoNear
聚合管道阶段要求您在集合上只有一个地理空间索引。您不能指定要查询的“字段名称”,因为命令格式要求集合中仅存在一个索引"2d"
或类型。"2dsphere"
无论如何,您应该只需要一个,但您应该意识到这一点。
强制选项$geoNear
当然是"near"
要搜索的坐标的参数,它"distanceField"
返回返回文档中的实际计算距离。"spherical"
索引是必需的,"2dsphere"
您应该使用它来进行精确测量,当然"distanceMultiplier"
是完全可选的,但会影响 . 指定的属性中返回的结果"distanceField"
。
根据您是否需要额外的查询条件或约束,还有其他选项,但这些是本练习的主要目的。
还要注意"longitude"
and"latitude"
顺序。这是MongoDB 地理空间查询正常工作所必需的。如果您的数据的这些值颠倒了(可能是从其他 API 获取的情况),那么您需要通过简单地颠倒存储的顺序或更好地完全转换为 GeoJSON 来更正它。
GeoJSON 转换
当然,另一种选择是简单地更新您的数据以实际使用 GeoJSON 格式:
var ops = [];
db.colllection.find({ "coordinate": { "$exists": true } }).forEach( doc => {
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": { "location": { "type": "Point", "coordinates": doc.coordinate } },
"$unset": { "coordinate": "" }
}
}
});
if ( ops.length >= 1000 ) {
db.collection.bulkWrite(ops);
ops = [];
}
})
if ( ops.length > 0 ) {
db.collection.bulkWrite(ops);
ops = [];
}
然后删除任何以前的索引并创建新索引:
db.collection.dropIndex({ "coordinate": "2dpshere" })
db.collection.createIndex({ "location": "2dsphere" })
当然collection
,在所有情况下都换成您的实际收藏名称。
然后,您也可以使用 GeoJSON 参数进行查询,并且只需按照标准以米为单位返回距离。
两者都是可行的选择,但真正使用 GeoJSON 应该保持与大多数其他外部库使用的兼容性,并保持对所有 MongoDB 地理空间查询操作的支持。
推荐阅读
- mysql - 如何从有 2 度关系的表中汇总交易金额
- javascript - 从 python API 获取 JSON
- angular - Angular 5 Excel Office App 通信未从对话框到任务窗格
- c# - WPF 从异步方法更新 UI
- python - Python散列二分搜索
- django - Django:加载 iframe 时如何获取父 url
- sqlite - 在 Android Studio 中将项目添加到第二个表 SQLite 什么都不做
- java - 如何在旋转后重新创建 LinearLayout 视图?
- javascript - 需要帮助自动刷新离线页面
- python - 从其他项目模块上的函数调用 JSON 数据以用于请求