【问题标题】:Calculate distance point to point in kilometer at aggregation pipeline stages在聚合管道阶段以公里为单位计算点到点的距离
【发布时间】:2018-10-10 03:01:38
【问题描述】:

聚合管道阶段返回的此文档:

//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

我看到这个link,但我不使用GeoJSON,我不想要proximity。我想计算点之间的距离我该怎么做? 我搜索了很多,但我在 mongoDB 中找不到任何结果!

我看到人们使用其他语言(例如 java 和 c# 和 java Script)来执行此操作,但没有人不使用 mongo 查询。 mongoDB可以吗?

【问题讨论】:

    标签: mongodb aggregation-framework


    【解决方案1】:

    实际上它在第一个链接中已经介绍过了,因为"distanceMultipler" 字段可用于在数据未存储在 GeoJSON 中时返回的“弧度”之间进行转换,并在任何情况下使用旧坐标对接受的格式。

    来自$geoNear 文档中的"distanceField" 选项:

    可选。乘以查询返回的所有距离的因子。例如,使用 distanceMultiplier 将球面查询返回的弧度通过乘以地球的半径转换为千米。

    来自Calculate Distance Using Spherical Geometry

    • 弧度的距离:将距离除以球体(例如地球)的半径,单位与距离测量值相同。
    • 弧度到距离:将弧度测量值乘以球体(例如地球)的半径,以单位制将距离转换为。

    地球的赤道半径约为 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""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 地理空间查询操作的支持。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-10
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多