使用 $lookup 时数组被重新排序

Array is reordered when using $lookup

我有这个聚合:

db.getCollection("users").aggregate([
  {
    "$match": {
      "_id": "5a708a38e6a4078bd49f01d5"
    }
  },
  {
    "$lookup": {
      "from": "user-locations",
      "localField": "locations",
      "as": "locations",
      "foreignField": "_id"
    }
  }
])

它运行良好,但有一件小事我不明白,我无法修复。 在查询输出中,locations 数组被 ObjectId 重新排序,我真的需要保持数据的原始顺序。

users 集合中的 locations 数组如下所示

'locations' : [
     ObjectId("5b55e9820b720a1a7cd19633"), 
     ObjectId("5a708a38e6a4078bd49ef13f")
], 

这是聚合后的结果:

'locations' : [
     {
        '_id' : ObjectId("5a708a38e6a4078bd49ef13f"),
        'name': 'Location 2'
     },
     {
        '_id' : ObjectId("5b55e9820b720a1a7cd19633"),
        'name': 'Location 1'
     }
],

我在这里错过了什么?我真的不知道如何处理这个问题。 可以推一下吗?

从这个closed bug report:

When using $lookup, the order of the documents returned is not guaranteed. The documents are returned in "natural order" - as they are encountered in the database. The only way to get a guaranteed consistent order is to add a $sort stage to the query.

基本上任何 Mongo query/pipeline 的工作方式是 returns 按照它们匹配的顺序记录文件,这意味着不能保证“正确”的顺序,尤其是在使用 indes 的情况下涉及。

您应该按照建议添加一个 $sort 阶段,如下所示:

db.collection.aggregate([
  {
    "$match": {
      "_id": "5a708a38e6a4078bd49f01d5"
    }
  },
  {
    "$lookup": {
      "from": "user-locations",
      "let": {
        "locations": "$locations"
      },
      "pipeline": [
        {
          "$match": {
            "$expr": {
              "$setIsSubset": [
                [
                  "$_id"
                ],
                "$$locations"
              ]
            }
          }
        },
        {
          $sort: {
            _id: 1 // any other sort field you want.
          }
        }
      ],
      "as": "locations",
    }
  }
])

您还可以保留您正在使用的原始 $lookup 语法,只使用 $unwind$sort$group 来恢复结构。

$lookup 不保证结果文档的顺序,您可以尝试一种管理文档自然顺序的方法,

  • $unwind解构locations数组并添加自动index数字将从0开始,
  • $lookup 位置
  • $set 到 select 来自 locations
  • 的第一个元素
  • $sortindex 字段升序排列
  • $group 通过 _id 并重构 locations 数组
db.users.aggregate([
  { $match: { _id: "5a708a38e6a4078bd49f01d5" } },
  {
    $unwind: {
      path: "$locations",
      includeArrayIndex: "index"
    }
  },
  {
    $lookup: {
      from: "user-locations",
      localField: "locations",
      foreignField: "_id",
      as: "locations"
    }
  },
  { $set: { locations: { $arrayElemAt: ["$locations", 0] } } },
  { $sort: { index: 1 } },
  {
    $group: {
      _id: "$_id",
      locations: { $push: "$locations" }
    }
  }
])

Playground