【问题标题】:Aggregate $lookup does not return elements original array order聚合 $lookup 不返回元素原始数组顺序
【发布时间】:2019-07-28 18:00:08
【问题描述】:

查询返回元素在集合中的放置顺序,忽略初始数组的顺序。这会影响我们系统的功能。是否有任何额外的命令可以将其按正确的顺序排列?有什么解决方法吗?

下面是一个简单的例子:

Collection1 文档

{
  "_id":ObjectId("5c781752176c512f180048e3"),
  "Name":"Pedro",
  "Classes":[
    {"ID": ObjectId("5c7af2b2f6f6e47c9060d7ce") },
    {"ID": ObjectId("5c7af2bcf6f6e47c9060d7cf") },
    {"ID": ObjectId("5c7af2aaf6f6e47c9060d7cd") }
  ]
}

Collection2 文档

{
  "_id":ObjectId("5c7af2aaf6f6e47c9060d7cd"),
  "variable1":"A"
},

{
  "_id": ObjectId("5c7af2b2f6f6e47c9060d7ce"),
  "variable1":"B"
},

{
  "_id": ObjectId("5c7af2bcf6f6e47c9060d7cf"),
  "variable1":"C"
}

查询:

aggregate(
  pipeline = '[
  {"$match": {"_id": {"$oid": "5c781752176c512f180048e3"}}},
  {"$lookup": {"from": "collection2", "localField": "Classes.ID", "foreignField": "_id", "as": "Collection2_doc"}}
  ]'
)

返回:

结果顺序:

[
    {
      "_id":ObjectId("5c7af2aaf6f6e47c9060d7cd"),
      "variable1":"A"
    },
    {
      "_id": ObjectId("5c7af2b2f6f6e47c9060d7ce"),
      "variable1":"B"
    },
    {
      "_id": ObjectId("5c7af2bcf6f6e47c9060d7cf"),
      "variable1":"C"
    }
]

预期顺序(第一个文档数组顺序):

[
    {
      "_id": ObjectId("5c7af2b2f6f6e47c9060d7ce"),
      "variable1":"B"
    },
    {
      "_id": ObjectId("5c7af2bcf6f6e47c9060d7cf"),
      "variable1":"C"
    },
    {
      "_id":ObjectId("5c7af2aaf6f6e47c9060d7cd"),
      "variable1":"A"
    }
]

是否有任何额外的命令 ex. $sort 可以用来按照原始数组顺序返回它吗?

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    这是 $lookup 实现的“设计”。 实际上发生了什么“幕后”是MongoDB internall$lookup中的参数转换为新的表达 使用$expr$in 格式化。即使在实现这种表现力 形式之前的版本中,“值数组” 的内部机制也非常相似。

    这里的解决方案是保留原始数组的副本作为重新排序“joined”项的参考:

    collection.aggregate([
      {"$match": {"_id": ObjectId("5c781752176c512f180048e3") }},
      {"$lookup": {
        "from": "collection2",
        "let": { "classIds": "$Classes.ID" },
        "pipeline": [
          { "$match": {
            "$expr": { "$in": [ "$_id", "$$classIds" ] }
          }},
          { "$addFields": {
            "sort": {
              "$indexOfArray": [ "$$classIds", "$_id" ]
            }
          }},
          { "$sort": { "sort": 1 } },
          { "$addFields": { "sort": "$$REMOVE" }}
        ],
        "as": "results"
      }}
    ])
    

    或者通过旧的$lookup 用法:

    collection.aggregate([
      {"$match": {"_id": ObjectId("5c781752176c512f180048e3") }},
      {"$lookup": {
        "from": "collection2",
        "localField": "Classes.ID",
        "foreignField": "_id",
        "as": "results"
      }},
      { "$unwind": "$results" },
      { "$addFields": {
        "sort": {
          "$indexOfArray": [ "$Classes.ID", "$results._id" ]
        }
      }},
      { "$sort": { "_id": 1, "sort": 1 } },
      { "$group": {
        "_id": "$_id",
        "Name": { "$first": "$Name" },
        "Classes": { "$first": "$Classes" },
        "results": { "$push": "$results" }
      }}
    ])
    

    两种变体产生相同的输出:

    {
            "_id" : ObjectId("5c781752176c512f180048e3"),
            "Name" : "Pedro",
            "Classes" : [
                    {
                            "ID" : ObjectId("5c7af2b2f6f6e47c9060d7ce")
                    },
                    {
                            "ID" : ObjectId("5c7af2bcf6f6e47c9060d7cf")
                    },
                    {
                            "ID" : ObjectId("5c7af2aaf6f6e47c9060d7cd")
                    }
            ],
            "results" : [
                    {
                            "_id" : ObjectId("5c7af2b2f6f6e47c9060d7ce"),
                            "variable1" : "B"
                    },
                    {
                            "_id" : ObjectId("5c7af2bcf6f6e47c9060d7cf"),
                            "variable1" : "C"
                    },
                    {
                            "_id" : ObjectId("5c7af2aaf6f6e47c9060d7cd"),
                            "variable1" : "A"
                    }
            ]
    }
    

    一般概念是使用 $indexOfArray"joined" 内容中的 _id 值进行比较,以找到它在原始内容中的 "index" 位置来自"$Classes.ID" 的源数组。不同的$lookup 语法变体对于您如何访问此副本 以及您如何基本重构具有不同的方法。

    $sort 当然设置实际文档的顺序,要么是在管道处理中 用于表达形式,要么是通过$unwind 的公开文档。在您使用$unwind 的地方,您将使用$group 回到原始文档表单。

    注意:这里的使用示例至少依赖于 MongoDB 3.4 的 $indexOfArray$$REMOVE 与 MongoDB 3.6 一致,expressive $lookup .

    还有其他方法可以为以前的版本重新排序数组,但这些方法在Does MongoDB's $in clause guarantee order 上进行了更详细的演示。实际上,您目前应该作为生产 MongoDB 版本运行的最低限度是 3.4 版本。

    请参阅 MongoDB 服务器 下的 Support Policy,了解支持的版本和结束日期的完整详细信息。

    【讨论】:

    • 这个答案是 100% 错误的。不会发生 $lookup 到 $expr 和 $in 的转换。它相当于使用 $in 进行常规查找。这不是一个很好的解决方法 - 不覆盖原始数组(以保留顺序)然后将其与从 $lookup 文档数组返回的合并更容易。这也迎合了数组与其他集合中的所有元素不匹配的情况。
    • @AsyaKamsky 你能用上面的例子回答一下吗?
    猜你喜欢
    • 2021-08-04
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 2019-02-02
    • 2011-11-12
    • 1970-01-01
    • 1970-01-01
    • 2016-07-31
    相关资源
    最近更新 更多