【问题标题】:Mongo aggregation use $sort in conjunction with $geoNearMongo 聚合将 $sort 与 $geoNear 结合使用
【发布时间】:2023-02-10 18:32:05
【问题描述】:

语境 :

我正在尝试构建一个显示 POI 的架构,随着时间的推移,这些 POI 可以位于不同的已知位置。

我有 2 个系列,

{
  _id: ObjectId,
  name: string
}

地点

  _id: ObjectId,
  point: {
    type: 'Point',
    coordinates: Array<number>
  },
  poi: ObjectId // Reference to Poi

用例:

所以我正在尝试构建一个查询

  • 输入中心坐标+半径
  • 并返回比半径内匹配的Pois
  • 只有他们最近的位置
  • 按距离排序

理想情况下,具有以下输出:

[
  {
    _id: ObjectId(AE54232),
    name: 'Some poi',
    location: {
      _id: ObjectId(BFE5423),
      point: {
        type: 'Point',
        coordinates: [3, 50]
      },
      distance: 3
    }
  }
]

试图

仔细阅读文档,我使用了这个组合:

      // Keep only locations within radius,
      // output 'distance'
      // and sort by distance
      {
        $geoNear: {
          near: nearCenter,
          key: 'point',
          distanceField: 'distance',
          maxDistance: nearRadius,
          spherical: true,
        },
      },
      // Keep only first (assumed 'nearest')
      // location of each poi
      {
        $group: {
          _id: '$poi',
          location: {
            $first: '$$ROOT'
          }
        }
      },
      // Retrieve poi
      {
        $lookup: {
          from: 'pois',
          localField: '_id',
          foreignField: '_id',
          as: 'poi',
        },
      },
      // Flatten poi
      {
        $unwind: {
          path: '$poi',
        },
      },
      // Push poi at the root,
      // and put location inside 'location'
      {
        $replaceRoot: {
          newRoot: {
            $mergeObjects: [
              "$poi",
              { location: "$location" },
            ]
          },
        }
      },

所以总结一下:

  • $geoNear
  • $first(by poi)
  • $lookup(poi)
  • $unwind(poi)
  • $replaceRoot(poi { location })

麻烦

我遇到了一种奇怪的行为,查询基本上可以正常工作;除了它不是按距离排序:pois 和他们的location 以不稳定和非确定性的顺序出现!

我试着一步一步地评论每一步,显然这是导致“洗牌”的$first。这是令人惊讶的,因为文档指出:

$geoNear

输出文档为了从最近到最远的指定点。

$first

返回将表达式应用于一组文档中的第一个文档所产生的值。只有当文件有意义有明确的顺序.

修复尝试

我的想法是 $first 期望实际的 $sort 而不是隐式的 $geoNear 排序;所以我试着像这样在两者之间插入一个$sort

      {
        $sort: {
          'distance': 1,
        },
      },

像这样介于两者之间:

  • $geoNear
  • $sort(distance) <== 这里
  • $first(by poi)
  • $lookup(poi)
  • $unwind(poi)
  • $replaceRoot(poi { location })

但它给了我完全相同的结果!


唯一有用的是在最后添加一个$sort,就像这样

      {
        $sort: {
          'location.distance': 1,
        },
      },
  • $geoNear
  • $first(by poi)
  • $lookup(poi)
  • $unwind(poi)
  • $replaceRoot(poi { location })
  • $sort(location.distance) <== 这里

但我担心它在大型数据集上可能会出现性能问题

问题

有什么办法可以实现这个逻辑

  • 过滤$geoNear(保持距离)
  • $group by referenced document,只保留“最近的”

不丢失 $geoNear 订单?

【问题讨论】:

  • 由于没有样本数据,问题不是很清楚。多个位置可以引用同一个poi吗?如果您提供一些示例文档以及它们的预期结果,将会更清楚
  • 如果每个poi 可以有几个位置,而不是分组后,poidistance 的排序无效。你应该在$group阶段之后再次按distance排序

标签: mongodb group-by aggregation-framework gis geonear


【解决方案1】:

如果每个poi 可以有几个位置,那么:

  {
        $geoNear: {
          near: nearCenter,
          key: 'point',
          distanceField: 'distance',
          maxDistance: nearRadius,
          spherical: true,
        },
      },
      // at this point you have all locations matching the criteria, sorted by `distance`
      // Keep only first (assumed 'nearest')
      // location of each poi
      {
        $group: {
          _id: '$poi',
          location: {
            $first: '$$ROOT'
          }
        }
      },
      // at this point you have one location and its distance from `nearCenter per each `poi`
      {
        $lookup: {
          from: 'pois',
          localField: '_id',
          foreignField: '_id',
          as: 'poi',
        },
      },
      // until here you retrieved the `poi` as `poi`
      {$sort: {distance: -1}}
      // now the `poi`s are sorted by distance
      {
        $replaceRoot: {
          newRoot: {
            $mergeObjects: [
              {$first: "$poi"},
              { location: "$location" },
            ]
          },
        }
      }
      // Now the answer is formatted (no need to $unwind since you have only one item in the array)

【讨论】:

    猜你喜欢
    • 2015-04-25
    • 2015-12-21
    • 1970-01-01
    • 2014-08-09
    • 2013-04-29
    • 2016-02-10
    • 2021-03-26
    • 2023-03-21
    • 2018-08-25
    相关资源
    最近更新 更多