mongodb - 使用 MongoDB 将文档中数组的每个元素链接到另一个文档的数组中的相应元素
问题描述
使用 MongoDB 4.2 和 MongoDB Atlas 测试聚合管道。
我有这个产品集合,包含具有这种模式的文档:
{
"name": "TestProduct",
"relatedList": [
{id:ObjectId("someId")},
{id:ObjectId("anotherId")}
]
}
然后是这个城市集合,包含具有这种模式的文档:
{
"name": "TestCity",
"instructionList": [
{ related_id: ObjectId("anotherId"), foo: bar},
{ related_id: ObjectId("someId"), foo: bar}
{ related_id: ObjectId("notUsefulId"), foo: bar}
...
]
}
我的目标是加入两个集合以输出类似这样的内容(操作是从城市文档中的指令列表中挑选每个相关对象,将其放入产品文档的相关列表中):
{
"name": "TestProduct",
"relatedList": [
{ related_id: ObjectId("someId"), foo: bar},
{ related_id: ObjectId("anotherId"), foo: bar},
]
}
我尝试使用 $lookup 运算符进行聚合,如下所示:
$lookup:{
from: 'cities',
let: {rId:'$relatedList._id'},
pipeline: [
{
$match: {
$expr: {
$eq: ["$instructionList.related_id", "$$rId"]
}
}
},
]
}
但它不起作用,我对这种复杂的管道语法有点迷失了。
编辑
通过在两个数组上使用 unwind :
{
{$unwind: "$relatedList"},
{$lookup:{
from: "cities",
let: { "rId": "$relatedList.id" },
pipeline: [
{$unwind:"$instructionList"},
{$match:{$expr:{$eq:["$instructionList.related_id","$$rId"]}}},
],
as:"instructionList",
}},
{$group: {
_id: "$_id",
instructionList: {$addToSet:"$instructionList"}
}}
}
我能够实现我想要的,但是,我根本没有得到一个干净的结果:
{
"name": "TestProduct",
instructionList: [
[
{
"name": "TestCity",
"instructionList": {
"related_id":ObjectId("someId")
}
}
],
[
{
"name": "TestCity",
"instructionList": {
"related_id":ObjectId("anotherId")
}
}
]
]
}
我怎样才能将所有内容分组以像我原来的问题所说的那样干净?同样,我完全迷失了聚合框架。
解决方案
我相信你只需要 $unwind 数组来查找关系,然后 $group 来重新收集它们。也许是这样的:
.aggregeate([
{$unwind:"relatedList"},
{$lookup:{
from:"cities",
let:{rId:"$relatedList.id"}
pipeline:[
{$match:{$expr:{$eq:["$instructionList.related_id", "$$rId"]}}},
{$unwind:"$instructionList"},
{$match:{$expr:{$eq:["$instructionList.related_id", "$$rId"]}}},
{$project:{_id:0, instruction:"$instructionList"}}
],
as: "lookedup"
}},
{$addFields: {"relatedList.foo":"$lookedup.0.instruction.foo"}},
{$group: {
_id:"$_id",
root: {$first:"$$ROOT"},
relatedList:{$push:"$relatedList"}
}},
{$addFields:{"root.relatedList":"$relatedList"}},
{$replaceRoot:{newRoot:"$root"}}
])
每个阶段的一点点:
- $unwind 为数组的每个元素复制整个文档,用单个元素替换数组
- 然后 $lookup 可以分别考虑每个元素。$lookup.pipeline 中的阶段
:$match 所以我们只展开具有匹配 ID
b 的文档。$unwind 数组,以便我们可以考虑单个元素
c. 重复 $match 所以我们只剩下匹配的元素(希望只有 1 个) - $addFields
foo
将从查找中检索到的字段分配给来自的对象relatedList
- $group 将所有具有相同 _id 的文档(即从单个原始文档展开)收集在一起,将第一个文档存储为“根”,并将所有相关列表元素推回数组中
- $addFields 将相关列表移动到根目录
- $replaceRoot 返回
root
,它现在应该是foo
添加到每个relatedList
元素的匹配项的原始文档
推荐阅读
- c++ - 从第 3 方库中导出 cpp 错位函数
- python - 熊猫数据框上的两个条件查询之间的区别?
- azure - 带有延续令牌的 Azure 逻辑应用配置
- java - 不影响任何内容的错误“org.postgresql.Driver 不是有效的 javax.sql.DataSource 实现”。如何删除它?
- java - cassandra3.0中触发器的实现
- sql - 查找具有连续行的行
- java - 如何为 Recyclerview 中的所有 imageView 添加相同的 onClick?
- vue.js - 显示重定向到链接的图标,而不是 bootstrap-vue 表中的数据值
- jupyter-notebook - 更改正在运行的 ipython 笔记本的 conda 环境
- css - 如何在 3 列模板中将 2 列居中