聚合$ lookup不返回元素原始数组顺序

时间:2019-03-06 23:27:02

标签: mongodb mongodb-query aggregation-framework

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

以下是一个简单的示例:

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"
    }
]

例如,是否还有其他命令。 $sort是否可以按照原始数组顺序返回它?

1 个答案:

答案 0 :(得分:2)

这是$lookup实现的“设计使然”。实际上 “幕后” 中发生的事情是MongoDB internall $lookup中的参数转换为新的 expressive < / em>格式,使用$expr$in。即使在实现这种 expressive 形式之前的版本中,“值数组” 的内部机制实际上也是相同的。

这里的解决方案是维护原始数组的副本,以作为对“ 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返回原始文档表单。

  

注意:此处的用法示例至少取决于$indexOfArray的MongoDB 3.4,而$$REMOVE与MongoDB 3.6一样,与富有表现力的 $lookup

     

还有其他方法可以对先前版本的数组进行重新排序,但是这些方法在Does MongoDB's $in clause guarantee order中有更详细的说明。实际上,当前作为生产MongoDB版本运行的最低要求是3.4版本。

     

有关受支持的发行版和结束日期的详细信息,请参见 MongoDB Server 下的Support Policy