mongodb - 如何编写聚合管道以获取合并文档作为文档插入 MongoDB 上的新字段?
问题描述
我是 MongoDB 的新手,我会用一个例子来展示我的问题。设想:
- 3个集合;
收藏_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 管道工具来实现呢?我以前从未写过管道,也不知道使用这些功能。
解决方案
你不能编写一个一次性更新的聚合管道,因为 MongoDb 4.2supports
只有管道中的这些阶段 -
- $addFields
- $set
- $项目
- $未设置
- $replaceRoot
- $replaceWith
并且因为您想链接集合 1 和集合 2 中的文档,所以不能将 update 与聚合管道一起使用。
所以一种方法如下 -
第一步是获取链接的数据,为此首先对链接的文档进行聚合,collection_1
然后使用运算符获取第一个找到的文档,lookup
collection_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);
}
})();
推荐阅读
- java - 如何连接到蓝牙服务器并发现服务?
- elasticsearch - 如何优化 Elasticsearch 的结果?
- ansible - 如何使用ansible对远程系统中的文件进行密码保护?
- nativescript - Nativescript:如何创建类似 Angular 路由器链接的行为
- python-3.x - 如何修复 ----- ./darknet:加载共享库时出错:libcudart.so.10.0:无法打开共享对象文件:没有这样的文件或目录
- c# - C#.NET MVC-HTTP 错误 403.14 - 禁止 Web 服务器配置为不列出此目录的内容
- python-3.x - 尝试从 json 文件加载刮板 itemloader 设置
- python - 如何修改 python 警告?
- c++ - 行抑制状态错误 C4703 使用了可能未初始化的局部指针变量 'back'
- c++ - 警告 C6031 返回值在宏扩展中被忽略