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

Mongodb 聚合管道中嵌入文档集合中的参考文档字段,mongodb,mongodb-query,Mongodb,Mongodb Query,我试图在管道阶段合并两个数组字段,它们位于嵌入文档集合中。但我一直在研究如何引用嵌入文档的两个“内部”数组 藏品 预期结果 方法 到目前为止,我尝试的总体方法是: (带有2个硬编码阵列,用于测试) 这将产生预期的结果: [{ name : "first", docs : [ { a1 : ["a", "b"], a2 : ["c"],

我试图在管道阶段合并两个数组字段,它们位于嵌入文档集合中。但我一直在研究如何引用嵌入文档的两个“内部”数组

藏品 预期结果 方法 到目前为止,我尝试的总体方法是: (带有2个硬编码阵列,用于测试)

这将产生预期的结果:

[{
    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
        }
    ]
}]
考虑 我不能使用
更新
查询,因为不能更改原始文档。因此,这个必须在
聚合
管道中实现

在这一点上,我尽量避免使用
unwind
操作,因为这将对性能产生重大影响。实际文档的根包含相当多的(变量)字段;在
放松
后进行
分组
阶段相当复杂。 (为了便于阅读,该示例已大大简化)


我正在使用MongoDB
v4.4

您可以执行以下操作

  • 首先
    $unwind
    展开文档数组
  • 因为a1和a2是动态的,所以我们将其放入数组中。(如果我们使用此选项,可以在输出中构造多个动态键)
  • 然后
    $reduce
    将数据添加到数组中
  • 并将其重新组合以获得所需的输出
  • 聚合脚本是

    [
      {
        "$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"
            }
          }
        }
      }
    ]
    

    工作

    我想这样就可以了,如果我缺少什么,请告诉我:

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

    我尽量避免
    展开
    ,因为这会迫使我重新组合文档。这更复杂,因为我的实际文档在其根目录下包含许多(变量)属性。问题更多的是关于如何引用嵌入文档中的字段。我也尝试了不同的方法,但我觉得无论如何都需要使用group。但您可以按
    $$ROOT
    对其进行分组,并用数据替换。但我急切地等待一个不使用unwind和groupI的回复,我不确定,但也许可以帮上忙——因为您使用的是MongoDB v4.4+。我希望它可以通过内置功能解决。但是
    $函数
    操作符完美地解决了这个问题,对性能的影响可以接受。谢谢
    [{
        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"] }
        }
    }
    
    [
      {
        "$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"
            }
          }
        }
      }
    ]
    
    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"
             }
          }
       }
    }])