MongoDB:在聚合中使用 $lookup 在嵌套文档上填充数组
MongoDB: populating arrays on nested document with $lookup in aggregation
我有这三个集合,我正在尝试用来自其他两个集合的 'joining in' 数据完全填充 messages_lists
集合的 messages
字段。
1. message_lists:
{
"_id" : ObjectId("5e96207ea4143b3204373534"),
"name" : "6bq32q8TcP",
"messages" : [
{
"inserted" : ISODate("2020-04-14T20:43:42.086+0000"),
"srcId" : "DNWLMSNW217480-20200414T194933Z"
},
{
"inserted" : ISODate("2020-04-16T17:40:22.585+0000"),
"srcId" : "DNWLMSNW217737-20200416T163914Z"
}
]
}
2。留言:
{
"_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
"srcId" : "DNWLMSNW217480-20200414T194933Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(-1),
"points" : [ 50080.028, 50080.029 ]
},
{
"direction" : NumberInt(1),
"points" : [ 50080.028, 50080.029 ]
}
]
},
{
"_id" : ObjectId("5e988b95ab20413033dbe9fb"),
"status" : "expired",
"srcId" : "DNWLMSNW217737-20200416T163914Z",
"versions" : [
{
"direction" : NumberInt(1),
"points" : [ 50060.096, 50060.097 ]
},
{
"direction" : NumberInt(-1),
"points" : [ 50060.096, 50060.097 ]
}
]
}
3。 loc_points:
{
"locId" : 50060.096,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
},
{
"locId" : 50060.097,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
},
{
"locId" : 50080.028,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260)
},
{
"locId" : 50080.029,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260),
}
我的mongoDB版本是3.4.4,这是我解决问题的尝试:
第 1 步:
我能够使用此查询成功完成第一个 $lookup 阶段:
db.getCollection("message_lists").aggregate([
{ $match: { name: '6bq32q8TcP' } },
{ $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
{ $lookup: {
from: 'messages',
localField: 'messages.srcId',
foreignField: 'srcId',
as: 'messages'
}},
{ $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
{ $group: {
_id: null,
messages: { $push: '$messages' }
}},
])
我对从上述查询中得到的结果非常满意。它看起来像这样,基本上设法用 messages
集合中包含的完整消息数据替换原始 messages_list
集合中对 messages
集合的引用 messages.srcId
:
{
"_id" : null,
"messages" : [
{
"_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
"srcId" : "DNWLMSNW217480-20200414T194933Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(-1),
"points" : [ 50080.028, 50080.029 ]
},
{
"direction" : NumberInt(1),
"points" : [ 50080.028, 50080.029 ]
}
]
},
{
"_id" : ObjectId("5e988b95ab20413033dbe9fb"),
"srcId" : "DNWLMSNW217737-20200416T163914Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(1),
"points" : [ 50060.096, 50060.097 ]
},
{
"direction" : NumberInt(-1),
"points" : [ 50060.096, 50060.097 ]
}
]
}
]
}
第 2 步: 此步骤的目标是使用 loc_points
集合中的数据填充 messages.versions.points
数组,以便获得最终结果结果应该是这样的:
这就是我需要的结果:
{
"_id" : null,
"messages" : [
{
"_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
"srcId" : "DNWLMSNW217480-20200414T194933Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(-1),
"points" : [
{
"locId" : 50080.028,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260)
},
{
"locId" : 50080.029,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260),
}
]
},
{
"direction" : NumberInt(1),
"points" : [
{
"locId" : 50080.028,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260)
},
{
"locId" : 50080.029,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260),
}
]
}
]
},
{
"_id" : ObjectId("5e988b95ab20413033dbe9fb"),
"srcId" : "DNWLMSNW217737-20200416T163914Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(1),
"points" : [
{
"locId" : 50060.096,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
},
{
"locId" : 50060.097,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
}
]
},
{
"direction" : NumberInt(-1),
"points" : [
{
"locId" : 50060.096,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
},
{
"locId" : 50060.097,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
}
]
}
]
}
]
}
为了到达那里,我的理解是我需要像这样执行额外的 $lookup:
{ $lookup: {
from: 'loc_points',
localField: 'messages.versions.points',
foreignField: 'locId',
as: 'messages.versions.points',
}},
{ $unwind: { path: '$messages.versions.points', preserveNullAndEmptyArrays: true }}
但是,经过一整天的研究并阅读了至少五个其他 Whosebug 线程后,我无法找到类似于我的深层嵌套文档结构的解决方案。
如果有更熟悉 mongoDB 聚合框架的人能帮助我,我将不胜感激!
提前致谢!!
编辑:
在 Joe 的下面建议之后,我在最后的 $group 语句之前添加了第二个 $lookup 。所以现在我的整体查询如下所示:
db.getCollection("message_lists").aggregate([
{ $match: { name: '6bq32q8TcP' } },
{ $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
{ $lookup: {
from: 'messages',
localField: 'messages.srcId',
foreignField: 'srcId',
as: 'messages'
}},
{ $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
{ $lookup: {
from: 'loc_points',
localField: 'messages.versions.points',
foreignField: 'locId',
as: 'messages.versions.points',
}},
{ $group: {
_id: null,
messages: { $push: '$messages' }
}}
])
但是,结果 messages.versions.points
作为一个空数组返回,所有其他 version
字段都丢失并且 messages.versions
甚至不再是数组:
{
"_id" : null,
"messages" : [
{
"_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
"srcId" : "DNWLMSNW217480-20200414T194933Z",
"status" : "expired",
"versions" : {
"points" : [
]
},
},
{
"_id" : ObjectId("5e988b95ab20413033dbe9fb"),
"srcId" : "DNWLMSNW217737-20200416T163914Z",
"status" : "expired",
"versions" : {
"points" : [
]
},
}
]
}
我只是想不通我需要如何修改 $group 语句才能正确填充 messages.versions.points
。任何帮助表示赞赏! (我知道,来自 MySQL 世界,我表明我还没有完全掌握聚合的概念,但与此同时,我正在努力寻找适合我的场景的教程)
$lookup
阶段 returns 管道中每个源文档的单个数组。
如果您在现有管道之后使用了您记下的附加 $lookup
阶段,它将添加一个数组,该数组将包含任何消息的任何版本所引用的点集。
为了获得为每条消息的每个版本填充的点数,必须展开这些点数。您已经有了消息,因此您需要展开版本和 运行 额外查找,然后按消息分组以将版本收集回数组,然后现有组收集消息。
试试这个
db.message_lists.aggregate([
{
$unwind: '$messages'
},
{
$lookup: {
from: 'messages',
localField: 'messages.srcId',
foreignField: 'srcId',
as: 'messages'
}
},
{
$project: {
_id: 1,
name: 1,
message: { $arrayElemAt: ["$messages", 0] },
}
},
{
$unwind: '$message.versions'
},
{
$unwind: '$message.versions.points'
},
{
$lookup: {
from: 'loc_point',
localField: 'message.versions.points',
foreignField: 'locId',
as: 'points'
}
},
{
$unwind: '$points'
},
{
$project: {
_id: 1,
name: 1,
message: {
_id: '$message._id',
srcId: '$message.srcId',
status: '$message.status',
versions: {
direction: "$message.versions.direction",
points: '$points'
}
}
}
},
{
$group: {
_id: { message_id: '$message._id', direction: '$message.versions.direction' },
name: { $first: '$name' },
srcId: { $first: '$message.srcId' },
status: { $first: '$message.status' },
points: {
$push: '$message.versions.points'
}
}
},
{
$group: {
_id: '$_id',
name: { $first: '$name' },
srcId: { $first: 'srcId' },
status: { $first: '$status' },
versions: {
$push: {
direction: '$_id.direction',
points: '$points'
}
}
}
},
{
$project: {
_id: "$_id.message_id",
name: 1,
srcId: 1,
status: 1,
versions: 1
}
},
{
$unwind: '$versions'
},
{
$group: {
_id: '$_id',
status: { $first: '$status' },
srcId: { $first: '$srcId' },
versions: { $push: '$versions' }
}
},
{
$group: {
_id: null,
messages: { $push: '$$ROOT' }
}
}
])
我有这三个集合,我正在尝试用来自其他两个集合的 'joining in' 数据完全填充 messages_lists
集合的 messages
字段。
1. message_lists:
{
"_id" : ObjectId("5e96207ea4143b3204373534"),
"name" : "6bq32q8TcP",
"messages" : [
{
"inserted" : ISODate("2020-04-14T20:43:42.086+0000"),
"srcId" : "DNWLMSNW217480-20200414T194933Z"
},
{
"inserted" : ISODate("2020-04-16T17:40:22.585+0000"),
"srcId" : "DNWLMSNW217737-20200416T163914Z"
}
]
}
2。留言:
{
"_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
"srcId" : "DNWLMSNW217480-20200414T194933Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(-1),
"points" : [ 50080.028, 50080.029 ]
},
{
"direction" : NumberInt(1),
"points" : [ 50080.028, 50080.029 ]
}
]
},
{
"_id" : ObjectId("5e988b95ab20413033dbe9fb"),
"status" : "expired",
"srcId" : "DNWLMSNW217737-20200416T163914Z",
"versions" : [
{
"direction" : NumberInt(1),
"points" : [ 50060.096, 50060.097 ]
},
{
"direction" : NumberInt(-1),
"points" : [ 50060.096, 50060.097 ]
}
]
}
3。 loc_points:
{
"locId" : 50060.096,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
},
{
"locId" : 50060.097,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
},
{
"locId" : 50080.028,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260)
},
{
"locId" : 50080.029,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260),
}
我的mongoDB版本是3.4.4,这是我解决问题的尝试:
第 1 步: 我能够使用此查询成功完成第一个 $lookup 阶段:
db.getCollection("message_lists").aggregate([
{ $match: { name: '6bq32q8TcP' } },
{ $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
{ $lookup: {
from: 'messages',
localField: 'messages.srcId',
foreignField: 'srcId',
as: 'messages'
}},
{ $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
{ $group: {
_id: null,
messages: { $push: '$messages' }
}},
])
我对从上述查询中得到的结果非常满意。它看起来像这样,基本上设法用 messages
集合中包含的完整消息数据替换原始 messages_list
集合中对 messages
集合的引用 messages.srcId
:
{
"_id" : null,
"messages" : [
{
"_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
"srcId" : "DNWLMSNW217480-20200414T194933Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(-1),
"points" : [ 50080.028, 50080.029 ]
},
{
"direction" : NumberInt(1),
"points" : [ 50080.028, 50080.029 ]
}
]
},
{
"_id" : ObjectId("5e988b95ab20413033dbe9fb"),
"srcId" : "DNWLMSNW217737-20200416T163914Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(1),
"points" : [ 50060.096, 50060.097 ]
},
{
"direction" : NumberInt(-1),
"points" : [ 50060.096, 50060.097 ]
}
]
}
]
}
第 2 步: 此步骤的目标是使用 loc_points
集合中的数据填充 messages.versions.points
数组,以便获得最终结果结果应该是这样的:
这就是我需要的结果:
{
"_id" : null,
"messages" : [
{
"_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
"srcId" : "DNWLMSNW217480-20200414T194933Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(-1),
"points" : [
{
"locId" : 50080.028,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260)
},
{
"locId" : 50080.029,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260),
}
]
},
{
"direction" : NumberInt(1),
"points" : [
{
"locId" : 50080.028,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260)
},
{
"locId" : 50080.029,
"road" : "A2",
"locCity" : NumberInt(690),
"locState" : NumberInt(260),
}
]
}
]
},
{
"_id" : ObjectId("5e988b95ab20413033dbe9fb"),
"srcId" : "DNWLMSNW217737-20200416T163914Z",
"status" : "expired",
"versions" : [
{
"direction" : NumberInt(1),
"points" : [
{
"locId" : 50060.096,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
},
{
"locId" : 50060.097,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
}
]
},
{
"direction" : NumberInt(-1),
"points" : [
{
"locId" : 50060.096,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
},
{
"locId" : 50060.097,
"road" : "A1",
"locCity" : NumberInt(445),
"locState" : NumberInt(260)
}
]
}
]
}
]
}
为了到达那里,我的理解是我需要像这样执行额外的 $lookup:
{ $lookup: {
from: 'loc_points',
localField: 'messages.versions.points',
foreignField: 'locId',
as: 'messages.versions.points',
}},
{ $unwind: { path: '$messages.versions.points', preserveNullAndEmptyArrays: true }}
但是,经过一整天的研究并阅读了至少五个其他 Whosebug 线程后,我无法找到类似于我的深层嵌套文档结构的解决方案。
如果有更熟悉 mongoDB 聚合框架的人能帮助我,我将不胜感激! 提前致谢!!
编辑: 在 Joe 的下面建议之后,我在最后的 $group 语句之前添加了第二个 $lookup 。所以现在我的整体查询如下所示:
db.getCollection("message_lists").aggregate([
{ $match: { name: '6bq32q8TcP' } },
{ $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
{ $lookup: {
from: 'messages',
localField: 'messages.srcId',
foreignField: 'srcId',
as: 'messages'
}},
{ $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
{ $lookup: {
from: 'loc_points',
localField: 'messages.versions.points',
foreignField: 'locId',
as: 'messages.versions.points',
}},
{ $group: {
_id: null,
messages: { $push: '$messages' }
}}
])
但是,结果 messages.versions.points
作为一个空数组返回,所有其他 version
字段都丢失并且 messages.versions
甚至不再是数组:
{
"_id" : null,
"messages" : [
{
"_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
"srcId" : "DNWLMSNW217480-20200414T194933Z",
"status" : "expired",
"versions" : {
"points" : [
]
},
},
{
"_id" : ObjectId("5e988b95ab20413033dbe9fb"),
"srcId" : "DNWLMSNW217737-20200416T163914Z",
"status" : "expired",
"versions" : {
"points" : [
]
},
}
]
}
我只是想不通我需要如何修改 $group 语句才能正确填充 messages.versions.points
。任何帮助表示赞赏! (我知道,来自 MySQL 世界,我表明我还没有完全掌握聚合的概念,但与此同时,我正在努力寻找适合我的场景的教程)
$lookup
阶段 returns 管道中每个源文档的单个数组。
如果您在现有管道之后使用了您记下的附加 $lookup
阶段,它将添加一个数组,该数组将包含任何消息的任何版本所引用的点集。
为了获得为每条消息的每个版本填充的点数,必须展开这些点数。您已经有了消息,因此您需要展开版本和 运行 额外查找,然后按消息分组以将版本收集回数组,然后现有组收集消息。
试试这个
db.message_lists.aggregate([
{
$unwind: '$messages'
},
{
$lookup: {
from: 'messages',
localField: 'messages.srcId',
foreignField: 'srcId',
as: 'messages'
}
},
{
$project: {
_id: 1,
name: 1,
message: { $arrayElemAt: ["$messages", 0] },
}
},
{
$unwind: '$message.versions'
},
{
$unwind: '$message.versions.points'
},
{
$lookup: {
from: 'loc_point',
localField: 'message.versions.points',
foreignField: 'locId',
as: 'points'
}
},
{
$unwind: '$points'
},
{
$project: {
_id: 1,
name: 1,
message: {
_id: '$message._id',
srcId: '$message.srcId',
status: '$message.status',
versions: {
direction: "$message.versions.direction",
points: '$points'
}
}
}
},
{
$group: {
_id: { message_id: '$message._id', direction: '$message.versions.direction' },
name: { $first: '$name' },
srcId: { $first: '$message.srcId' },
status: { $first: '$message.status' },
points: {
$push: '$message.versions.points'
}
}
},
{
$group: {
_id: '$_id',
name: { $first: '$name' },
srcId: { $first: 'srcId' },
status: { $first: '$status' },
versions: {
$push: {
direction: '$_id.direction',
points: '$points'
}
}
}
},
{
$project: {
_id: "$_id.message_id",
name: 1,
srcId: 1,
status: 1,
versions: 1
}
},
{
$unwind: '$versions'
},
{
$group: {
_id: '$_id',
status: { $first: '$status' },
srcId: { $first: '$srcId' },
versions: { $push: '$versions' }
}
},
{
$group: {
_id: null,
messages: { $push: '$$ROOT' }
}
}
])