聚合管道中的嵌入式文档集合中的参考文档字段

时间:2020-09-23 07:46:46

标签: mongodb mongodb-query

我正在尝试在管道阶段合并位于嵌入式文档集合中的两个数组字段。但是我一直坚持如何引用嵌入式文档的两个“ 内部”数组。

收藏集

[{
    name: "first",
    docs: [
        { a1: ["a", "b"], a2: ["c"] },
        { a1: ["d", "e"], a2: ["f"] }
    ]
},
{
    name: "second",
    docs: [
        { a1: [1, 2], a2: [3] },
        { a1: [4, 5], a2: [6] }
    ]
}]

预期结果

[{
    name: "first",
    docs: [
        { merged: ["a", "b", "c"] },
        { merged: ["d", "e", "f"] }
    ]
},
{
    name: "second",
    docs: [
        { merged: [1, 2, 3] },
        { merged: [4, 5, 6] }
    ]
}]

方法

到目前为止,我尝试的总体方法是: (带有2个硬编码数组用于测试)

db.getCollection("collection").aggregate([{
    $set: {
         "docs.merged": {
             $concatArrays: [["hello"], ["world"]]
         }
    }
}])

将产生预期的结果:

[{
    name : "first",
    docs : [
        {
            a1 : ["a", "b"],
            a2 : ["c"],
            merged : ["hello", "world"] // <- OK
        },
        {
            a1 : ["d", "e"],
            a2 : ["f"],
            merged : ["hello", "world"] // <- OK
        }
    ]
},{
    name : "second",
    docs : [
        {
            a1 : [1.0, 2.0],
            a2 : [3.0],
            merged : ["hello", "world"] // <- OK
        },
        {
            a1 : [4.0, 5.0],
            a2 : [6.0],
            merged : ["hello", "world"] // <- OK
        }
    ]
}]

但是我在掌握如何引用当前嵌入式文档中的字段方面遇到困难:

// Using the "$" reference causes following error:
// Invalid $set :: caused by :: FieldPath field names may not start with '$'.
{
    $set: {
         "docs.merged": { $concatArrays: ["$docs.$.a1", "$docs.$.a2"] }
    }
}

// $$this is only available with a MAP operator
{
    $set: {
         "docs.merged": { $concatArrays: ["$$this.a1", "$$this.a2"] }
    }
}

注意事项

我不能使用update查询,因为原始文档不得更改。因此,此必须必须在aggregate管道中实现。

我现在尽量避免使用unwind操作,因为这会对性能产生重大影响。 actual 文档的根部包含很多(可变)字段。在group之后进行unwind阶段非常复杂。 (该示例已大大简化了可读性)

我正在使用MongoDB v4.4

2 个答案:

答案 0 :(得分:1)

您可以执行以下操作。

  1. 首先$unwind将docs数组展平。
  2. 由于a1和a2是动态的,因此将其放入数组。 (如果使用此功能,则可以为您的输出构建多个动态键)。
  3. 然后$reduce将数据添加到数组中。
  4. 并重新组合以获得所需的输出。

聚合脚本是

[
  {
    "$unwind": "$docs"
  },
  {
    $project: {
      name: 1,
      data: {
        $objectToArray: "$docs"
      }
    }
  },
  {
    $project: {
      name: 1,
      data: {
        $reduce: {
          input: "$data",
          initialValue: [],
          in: {
            $concatArrays: [
              "$$this.v",
              "$$value"
            ]
          }
        }
      }
    }
  },
  {
    $group: {
      _id: "$_id",
      name: {
        $first: "$name"
      },
      docs: {
        $push: {
          merged: "$data"
        }
      }
    }
  }
]

工作Mongo playground

答案 1 :(得分:1)

我认为这可以做到,请让我知道我是否缺少任何东西:

db.collection.aggregate([{
   $project: {
      _id: 0,
      "name": 1,
      "docs": {
         $function: {
            body: function(docs) {
              docs.forEach(function(doc) {
                 var merged = [];
                 Object.keys(doc).forEach(function(k) {
                    merged = merged.concat(doc[k]);
                    delete doc[k];
                 });
                 doc.merged = merged;
              });
              return docs;
            },
            args: [ "$docs" ],
            lang: "js"
         }
      }
   }
}])