node.js - 在内部嵌套数组中分组 $lookup 结果,并在 MongoDB 中保持外部嵌套数组及其内容完整
问题描述
我有一个包含三层数组嵌套的集合,如下所示
_id: ObjectID('abc'),
sections: [
{
sectionId: "sec0",
sectionName: "ABC",
contents: [
{
contentId: 0,
tasks: [
{
taskId: ObjectID('task1')
}
//May contain 1-100 tasks
],
contentDescription: "Content is etc",
}
]
}
]
Sections 是一个对象数组,其中包含一个对象,每个对象都有一个 sectionId,contents 数组是一个对象数组,由 contentId、contentDescription 和嵌套的任务数组组成,其中包含一个包含 taskId 的对象。我正在应用 $lookup 运算符,以便将嵌套任务数组与任务集合连接起来,但我在文档重复方面遇到了问题,如下所示。
_id: ObjectID('abc'),
sections: [
{
sectionId: "sec0",
sectionName: "ABC",
contents: [
{
contentId: 0,
tasks: [
{
//Task Document of ID 1
}
],
contentDescription: "Content is etc",
}
]
}
]
_id: ObjectID('abc'),
sections: [
{
sectionId: "sec0",
sectionName: "ABC",
contents: [
{
contentId: 0,
tasks: [
{
//Task Document of ID 2
}
],
contentDescription: "Content is etc",
}
]
}
]
而所需的输出如下
_id: ObjectID('abc'),
sections: [
{
sectionId: "sec0",
sectionName: "ABC",
contents: [
{
contentId: 0,
tasks: [
{
//Task Document of ID 1
},
{
//Task Document of ID 2
},
{
//Task Document of ID 3
}
],
contentDescription: "Content is etc",
}
]
}
]
在集合中,sections 数组可能包含多个 section 对象,这些对象可能包含多个内容等等。有问题的架构是临时的,因为我们公司目前正在从现有数据库迁移到 MongoDB,因此架构重构是不可能的,我需要使用来自不同数据库的现有架构设计。
我尝试了以下方式
const contents= await sections.aggregate([
{
$match: { _id: id},
},
{ $unwind: '$sections' },
{
$unwind: {
path: '$sections.contents',
preserveNullAndEmptyArrays: true,
},
},
{
$unwind: {
path: '$sections.contents.tasks',
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: 'tasks',
let: { task_id: '$sections.contents.tasks.taskId' },
pipeline: [
{ $match: { $expr: { $eq: ['$_id', '$$task_id'] } } },
],
as: 'sections.contents.tasks',
},
},
{
$addFields: {
'sections.contents.tasks': {
$arrayElemAt: ['$sections.contents.tasks', 0],
},
},
},
{
$group: {
_id: '$_id',
exam: { $push: '$sections.contents.tasks' },
},
},
]);
而且我也无法使用 $group 聚合运算符
$group: {
_id: '$_id',
sections: {
sectionId : { $first: '$sectionId' },
sectionName: { $first: '$sectionName' },
contents: {
contentId: { $first: '$contentId' },
task: { $push: $sections.contents.tasks }
}
},
},
任何帮助或指示将不胜感激,我也在 SO 上进行了搜索,发现了这个但无法理解以下部分
{"$group":{
"_id":{"_id":"$_id","mission_id":"$missions._id"},
"agent":{"$first":"$agent"},
"title":{"$first":"$missions.title"},
"clients":{"$push":"$missions.clients"}
}},
{"$group":{
"_id":"$_id._id",
"missions":{
"$push":{
"_id":"$_id.mission_id",
"title":"$title",
"clients":"$clients"
}
}
}}
解决方案
所以你非常接近最终的解决方案,一个很好的“规则”要记住,如果你放松x
时间,你需要分组x
以正确恢复原始结构,如下所示:
db.collection.aggregate([
{
$match: {
_id: id
},
},
{
$unwind: "$sections"
},
{
$unwind: {
path: "$sections.contents",
preserveNullAndEmptyArrays: true,
},
},
{
$unwind: {
path: "$sections.contents.tasks",
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: "tasks",
let: {
task_id: "$sections.contents.tasks.taskId"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$_id",
"$$task_id"
]
}
}
},
],
as: "sections.contents.tasks",
},
},
{
$addFields: {
"sections.contents.tasks": {
$arrayElemAt: [
"$sections.contents.tasks",
0
],
},
},
},
{
$group: {
_id: {
contentId: "$sections.contents.contentId",
sectionId: "$sections.sectionId",
sectionName: "$sections.sectionName",
originalId: "$_id"
},
tasks: {
$push: "$sections.contents.tasks"
},
contentDescription: {
$first: "$sections.contents.contentDescription"
},
}
},
{
$group: {
_id: {
sectionId: "$_id.sectionId",
sectionName: "$_id.sectionName",
originalId: "$_id.originalId"
},
contents: {
$push: {
contentId: "$_id.contentId",
tasks: "$tasks",
contentDescription: "$contentDescription"
}
}
}
},
{
$group: {
_id: "$_id.originalId",
sections: {
$push: {
sectionId: "$_id.sectionId",
sectionName: "$_id.sectionName",
contents: "$contents"
}
}
}
}
])
但是,您的管道可以变得更清洁,因为它有 1 个冗余$unwind
阶段,也添加了一个冗余$group
阶段。我不会在这里发布整个固定管道,因为它已经是一个很长的帖子,但请随时在这里查看:Mongo Playground fixed
推荐阅读
- qnamaker - 在 qna maker 中从文件层次结构中加载 KB 中的问题和答案
- python - 由于冲突,无法使用 anaconda 安装 geopandas
- c# - ASP.NET Core API 从 body 绑定多个对象类型
- c - 在函数中保存修改后的数组
- android - 使用片段之间的参数正确初始化 ViewModel
- filebeat - Filebeat“加载配置文件时出错:yaml:没有找到预期的密钥”
- python - 制作一种方法检查数据框中的值是否为特定值
- r - 如何在 R 的整洁数据框中将组的平均值添加为新组?
- django - Django登录重定向到登录页面
- javascript - 如果用户登录或未登录,实现条件导航栏的策略