首页 > 解决方案 > 如何编写聚合管道以获取合并文档作为文档插入 MongoDB 上的新字段?

问题描述

我是 MongoDB 的新手,我会用一个例子来展示我的问题。设想:

收藏_1

{
    "_id": 1,
    "idLocal": 1023,
    "idType": 21 
},
{
    "_id": 2,
    "idLocal": 1029,
    "idType": 21 
},
{
    "_id": 3,
    "idLocal": 923,
    "idType": 22 
}

收藏_2

{
    "_id": 1,
    "idLOp": 1,
    "idType": 21    
},
{
    "_id": 2,
    "idOp": 5,
    "idType": 21 
}
{
    "_id": 3,
    "idOp": 1,
    "idType": 12 
}

因此,要在 Collection_3 上插入新文档,我想创建一个新文档,将 Collection_1 和 Collection_2 中的文档附加到以下条件:

idLocal (Collection_1) == idLocal (New doc to insert) // get desired doc from Collection_1
idOp (Collection_2) == idOp (New doc to insert) AND idType (Collection_1 ) == idType  (Collection_2) // get desired doc from Collection_2

要插入的示例文档

{
    "_id": 5,
    "idOp": 1,
    "idLocal": 1023
}

收藏_3

// new doc saved at Collection_3 result desired:

{
    "_id": 5,
    "idOP": 1,
    "idLocal": 1023,
    "Collection_1_doc": { 
          "_id": 1,
          "idLocal": 1023,
          "idType": 21  
    },
    "Collection_2_doc": {
           "_id": 1,
           "idLOp": 1,
           "idType": 21 
    }
}

那么,如何使用 MongoDB 管道工具来实现呢?我以前从未写过管道,也不知道使用这些功能。

标签: mongodbmongodb-queryaggregation-framework

解决方案


你不能编写一个一次性更新的聚合管道,因为 MongoDb 4.2supports只有管道中的这些阶段 -

  • $addFields
  • $set
  • $项目
  • $未设置
  • $replaceRoot
  • $replaceWith

并且因为您想链接集合 1 和集合 2 中的文档,所以不能将 update 与聚合管道一起使用。


所以一种方法如下 -

第一步是获取链接的数据,为此首先对链接的文档进行聚合,collection_1然后使用运算符获取第一个找到的文档,lookupcollection_2$arrayElemAt

var doc3 = {
    "_id": 5,
    "idOp": 1,
    "idLocal": 1023
};

db.Collection_1.aggregate([
    {
        $match: {
            idLocal: doc3.idLocal
        }
    },
    {
        $lookup: {
           from: 'Collection_2',
           pipeline: [
              { 
                $match: { 
                    $expr: { 
                        $eq: [ "$idOp",  doc.idOp ] 
                    }
                }
              }
           ],
           as: "Collection_2_data"
        }
    },
    {
        $project: {
            _id: 0,
            Collection_1: {
                _id: '$_id',
                idLocal: '$idLocal',
                idType: '$idType'
            },
            Collection_2: {
                $arrayElemAt: [
                    '$Collection_2_data',
                    0
                ]
            }
        }
    }
]);

MongoPlayground link, 这使 -

[
  {
    "Collection_1": {
      "_id": 1,
      "idLocal": 1023,
      "idType": 21
    },
    "Collection_2": {
      "_id": 3,
      "idOp": 1,
      "idType": 12
    }
  }
]

由于聚合查询的结果始终包装在数组中,因此请安全地访问第一个索引并将其附加到您的索引doc3并插入它。

假设您使用的是猫鼬,这是上述想法的代码 -

const mongoose = require('mongoose');

(async () => {
    try {
        let doc3 = {
            _id: 5,
            idOp: 1,
            idLocal: 1023
        };

        const pipeline = [
            {
                $match: {
                    idLocal: doc3.idLocal
                }
            },
            {
                $lookup: {
                   from: 'Collection_2',
                   pipeline: [
                      { 
                        $match: { 
                            $expr: { 
                                $eq: [ "$idOp",  doc.idOp ] 
                            }
                        }
                      }
                   ],
                   as: "Collection_2_data"
                }
            },
            {
                $project: {
                    _id: 0,
                    Collection_1: {
                        _id: '$_id',
                        idLocal: '$idLocal',
                        idType: '$idType'
                    },
                    Collection_2: {
                        $arrayElemAt: [
                            '$Collection_2_data',
                            0
                        ]
                    }
                }
            }
        ];

        const linkedDocsResult = await Collection_1.aggregate(pipeline).exec();
        
        if (linkedDocsResult.length) {
            doc3 = Object.assign(Object.create(null), doc3, linkedDocsResult[0]);
        }
        
        await Collection_3.create(doc3);
    } catch (err) {
        console.error(err);
    }
})();

推荐阅读