上下文:

我正试图建立一个展示POI的架构,随着时间的推移,POI可以位于不同的已知位置.

我有两个Collection ,

POIS

{
  _id: ObjectId,
  name: string
}

位置

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

使用 case :

所以我正在try 构建一个查询,该查询

  • 采用中心坐标+半径作为输入
  • 并返回小于半径范围内的匹配POI
  • 只有他们最近的位置
  • 按距离排序

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

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

try

仔细阅读文档时,我使用了这样的组合:

      // Keep only 位置 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 })个个个

麻烦

我面临着一种奇怪的行为,查询基本上是工作的;除了它不是按距离排序:poilocation的顺序是不稳定的和不确定的!

我试着逐一 comments 每一步,显然这就是导致"洗牌"的$first步.这很令人惊讶,因为doctor 说:

$geoNear

输出距指定点最近到最远的文档in order.

$first

返回将表达式应用于一组文档组中的第一个文档所产生的值.只有当文档are in a defined order时才有意义.

修复try

我认为$first应该是实际的$sort,而不是隐式的$geoNear个个个排序;所以我try 在两者之间插入$sort,如下所示:

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

中间是这样的:

  • $geoNear个个个
  • $sort(distance)lt;==此处
  • $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)lt;==此处

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

问题

有什么方法可以实现这一逻辑吗

  • 过滤器$GeoNear(保持距离)
  • $GROUP BY REFERED DOCUMENT,仅保留‘最近的’

而不会失go $GeoNear订单?

推荐答案

如果每个poi可以具有较少的位置,则对它们进行分组可能会改变顺序,因此分组后的文档不再按distance排序.您可以在分组后按distance排序来求解它:

  {
        $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`. The grouping can change the order of documents 
      {
        $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)

Mongodb相关问答推荐

MongoDB$unionWith,如何 Select 特定文档

如何在Mongo中制作全覆盖索引

MongoDB聚合如何对对象数组中的值求和

mongoose正在抛出Schema的错误has';t已在next.js中注册

MongoDB - 来自先前匹配文档的聚合匹配字段

聚合 $accumulator + $project

MongoDB 存储大量指标/分析数据的方法

Pymongo API TypeError: Unhashable dict

Meteor mongo 驱动程序可以处理 $each 和 $position 运算符吗?

mongodb - 查找具有最接近整数值的文档

如何使用 mongoexport 导出排序数据?

通过 Spring Boot 应用程序访问 mongodb 时的身份验证错误

将数据插入 MongoDB - 没有错误,没有插入

如何在任意深度查找 MongoDB 字段名称

在 URL 中使用 ID(来自 mongo 的 ObjectId)是否安全?

从每个组中 Select 前 N 行

在 MongoDB 上分片 GridFS

如何仅通过一次调用将一组对象保存到mongoose数据库?

从 mongo 结果中删除 _id

PyMongo 中的警告消息:count is deprecated