Node.js 将合并查找结果聚合到主文档

Node.js 将合并查找结果聚合到主文档,node.js,mongodb,aggregation-framework,Node.js,Mongodb,Aggregation Framework,我创建了一个函数来缝合来自不同集合的2条记录: 第1条记录: { _id: objectId(1231242331233), acc: '12390', val2: 'asdasdas' } 收集2记录: { _id: objectId(989232382302308), isValid: '1', tf: '098789928', acc: '12390' } 为此,我提出了以下带有$lookup的聚合函数 Collection2.a

我创建了一个函数来缝合来自不同集合的2条记录:

第1条记录:

{
    _id: objectId(1231242331233),
    acc: '12390',
    val2: 'asdasdas'
}
收集2记录:

{
    _id: objectId(989232382302308),
    isValid: '1',
    tf: '098789928',
    acc: '12390'
}
为此,我提出了以下带有$lookup的聚合函数

Collection2.aggregate([
    {
        $lookup:
        {
            from: "Collection1",
            localField: "acc",
            foreignField: "acc",
            as: "acc_record"
        }
    }   
    {
        $out: 'Collection3'
    }
]);
这将生成具有以下结构的记录的集合3:

{
    _id: objectId(989232382302308),
    isValid: '1',
    tf: '098789928',
    acc: '12390',
    acc_record:[
        {
            _id: objectId(1231242331233),
            acc: '12390',
            val2: 'asdasdas'
        }
    ]
}
合并这两条记录的聚合函数是什么,而不是将Collection1记录放在json对象的更深层次,只放置不相等的元素并合并具有相同名称的元素

因此,最终记录结果将是:

{
        _id: objectId(989232382302308),
        isValid: '1',
        tf: '098789928',
        acc: '12390',
        val2: 'asdasdas'
}
附加模块:

和用于引用数组中的值,并将其升级为顶级对象中的值

如果您总是知道结果是“一对一”的,并且可以简单地从返回的第一个数组元素中获取值,那么这很好。如果它们是“一对多”,那么您也可以申请:

这对于“一对一”也是完全有效的,但是您应该注意,
\u id
在这里是“故意”删除的。原因是,对于“多个”结果,会为结果中返回的每个数组成员生成父文档的“多个”副本。由于
\u id
是“主键”,因此不能在“多个文档”中保持该值相同

因此,丢弃主键的意义在于,这样可以在写入时创建一个新值,而不会因“重复键错误”而失败。或者,如果希望将其保留为“引用”,则只需将
“$\u id”
值重命名为


对于更大的输出,我们可以使用一些技巧来“合并”MongoDB支持的特性。在当前版本中,这些是和来自MongoDB 3.4.4及更高版本:

Collection2.aggregate([
    { "$lookup":{
      "from": "Collection1",
      "localField": "acc",
      "foreignField": "acc",
      "as": "acc_record"
    }},
    { "$unwind": "$acc_record" },
    { "$replaceRoot": {
      "newRoot": {
        "$arrayToObject": {
          "$concatArrays": [
            { "$filter": {
              "input": { "$objectToArray": "$$ROOT" },
              "cond": { "$not": { "$in":  ["$$this.k", ["_id", "acc_record"] } }
            }},
            { "$filter": {
              "input": { "$objectToArray": "$acc_record" },
              "cond": { "$ne": ["$$this.k", "acc"] }
            }}
          ]
        }
      }
    }},
    { "$out": "Collection3" }
])
诀窍是将“根”文档和子数组内容都转换为单独的数组,过滤掉重叠的键并应用它们,使它们成为一个数组。然后,您可以应用“连接”结果,并通过将其转换为根文档

MongoDB 3.6 MongoDB 3.6使其更加简单,并引入了一些新功能,因此您可以真正做一些简单的事情,如:

Collection2.aggregate([
    { "$lookup":{
      "from": "Collection1",
      "localField": "acc",
      "foreignField": "acc",
      "as": "acc_record"
    }},
    { "$unwind": "$acc_record" },
    { "$replaceRoot": {
      "newRoot": {
        "$mergeObjects": [
          { "$arrayToObject": {
            "$filter": {
              "input": { "$objectToArray": "$$ROOT" },
              "cond": { "$not": { "$in":  ["$$this.k", ["_id", "acc_record","acc"] } },
            }
          },
          "$acc_record"
        ]
      }
    }},
    { "$out": "Collection3" }
]);
通常情况下,您仍然希望输入不需要的键,例如的目标字段名,很可能还有
“localField”
“foreignField”
值。因此,除非您准备添加另一个聚合阶段以完全删除该子项,否则您实际上不能仅使用
“$ROOT”
与子项的内容合并

因此,一般来说,这里并没有添加太多内容,当然,操作符的命名使代码的意图非常清楚。

加载项:

和用于引用数组中的值,并将其升级为顶级对象中的值

如果您总是知道结果是“一对一”的,并且可以简单地从返回的第一个数组元素中获取值,那么这很好。如果它们是“一对多”,那么您也可以申请:

这对于“一对一”也是完全有效的,但是您应该注意,
\u id
在这里是“故意”删除的。原因是,对于“多个”结果,会为结果中返回的每个数组成员生成父文档的“多个”副本。由于
\u id
是“主键”,因此不能在“多个文档”中保持该值相同

因此,丢弃主键的意义在于,这样可以在写入时创建一个新值,而不会因“重复键错误”而失败。或者,如果希望将其保留为“引用”,则只需将
“$\u id”
值重命名为


对于更大的输出,我们可以使用一些技巧来“合并”MongoDB支持的特性。在当前版本中,这些是和来自MongoDB 3.4.4及更高版本:

Collection2.aggregate([
    { "$lookup":{
      "from": "Collection1",
      "localField": "acc",
      "foreignField": "acc",
      "as": "acc_record"
    }},
    { "$unwind": "$acc_record" },
    { "$replaceRoot": {
      "newRoot": {
        "$arrayToObject": {
          "$concatArrays": [
            { "$filter": {
              "input": { "$objectToArray": "$$ROOT" },
              "cond": { "$not": { "$in":  ["$$this.k", ["_id", "acc_record"] } }
            }},
            { "$filter": {
              "input": { "$objectToArray": "$acc_record" },
              "cond": { "$ne": ["$$this.k", "acc"] }
            }}
          ]
        }
      }
    }},
    { "$out": "Collection3" }
])
诀窍是将“根”文档和子数组内容都转换为单独的数组,过滤掉重叠的键并应用它们,使它们成为一个数组。然后,您可以应用“连接”结果,并通过将其转换为根文档

MongoDB 3.6 MongoDB 3.6使其更加简单,并引入了一些新功能,因此您可以真正做一些简单的事情,如:

Collection2.aggregate([
    { "$lookup":{
      "from": "Collection1",
      "localField": "acc",
      "foreignField": "acc",
      "as": "acc_record"
    }},
    { "$unwind": "$acc_record" },
    { "$replaceRoot": {
      "newRoot": {
        "$mergeObjects": [
          { "$arrayToObject": {
            "$filter": {
              "input": { "$objectToArray": "$$ROOT" },
              "cond": { "$not": { "$in":  ["$$this.k", ["_id", "acc_record","acc"] } },
            }
          },
          "$acc_record"
        ]
      }
    }},
    { "$out": "Collection3" }
]);
通常情况下,您仍然希望输入不需要的键,例如的目标字段名,很可能还有
“localField”
“foreignField”
值。因此,除非您准备添加另一个聚合阶段以完全删除该子项,否则您实际上不能仅使用
“$ROOT”
与子项的内容合并


因此,一般来说,除了操作符的命名使代码的意图非常清晰之外,这里的添加并不多。

我是否需要引用$project中Collection2中的所有记录?Collection2表示只有一个例子,我的实际集合由超过50个属性的记录组成。让这项工作变得麻烦。@Joaofilipeclementematins仍在写作。我要说了。在将来,你实际上需要在“你的问题”中详细说明这些细节。这些是使用
$out
完成此操作的完整方法。您还需要使用游标并对其进行迭代以转换输出,然后进行回写。我是否需要引用$project中Collection2中的所有记录?Collection2表示只有一个例子,我的实际集合由超过50个属性的记录组成。让这项工作变得麻烦。@Joaofilipeclementematins仍在写作。我要说了。在将来,你实际上需要在“你的问题”中详细说明这些细节。这些是使用
$out
完成此操作的完整方法。您还可以使用游标并对其进行迭代以转换输出,然后进行回写。在提供的答案中是否有您认为无法解决问题的内容?