MongoDB中如何用$lookup关联嵌套数组中的歌曲详细数据?
解决MongoDB中关联艺术家、专辑与歌曲的聚合查询问题
没问题,我来帮你搞定这个嵌套关联的需求~你已经完成了艺术家和专辑的关联,要同时获取每个专辑对应的完整歌曲数据,这里有两种实用方案,取决于你的MongoDB版本:
方法一:嵌套$lookup(MongoDB 3.6+ 推荐)
这种方式不用折腾数组的展开和重组,直接在关联专辑的$lookup里嵌套另一个$lookup来关联歌曲,代码更简洁高效:
db.artists.aggregate([ { $lookup: { from: "albums", localField: "albums", foreignField: "_id", as: "albums", // 给每个关联到的专辑单独执行关联歌曲的逻辑 pipeline: [ { $lookup: { from: "songs", localField: "songs", foreignField: "_id", as: "songs" } } ] } } ]).pretty()
逻辑说明:
- 外层的
$lookup先把艺术家文档里的albums(ObjectId列表)替换成完整的专辑文档数组 - 内层的
pipeline是MongoDB 3.6新增的功能,允许我们对每一条关联到的专辑文档,再执行一次小的聚合操作——这里就是用另一个$lookup把专辑里的songs(ObjectId列表)替换成完整的歌曲文档数组
方法二:$unwind + $lookup + $group(兼容旧版本MongoDB)
如果你的MongoDB版本低于3.6,没法用嵌套pipeline的话,就用「展开数组→关联歌曲→重组数组」的经典流程:
db.artists.aggregate([ // 第一步:先关联艺术家和专辑 { $lookup: { from: "albums", localField: "albums", foreignField: "_id", as: "albums" } }, // 第二步:把专辑数组展开,让每个专辑单独成为一条文档 { $unwind: "$albums" }, // 第三步:给每个专辑关联对应的歌曲 { $lookup: { from: "songs", localField: "albums.songs", foreignField: "_id", as: "albums.songs" } }, // 第四步:按艺术家ID分组,把专辑重新拼成数组,同时保留艺术家的其他信息 { $group: { _id: "$_id", name: { $first: "$name" }, started_in: { $first: "$started_in" }, active: { $first: "$active" }, country: { $first: "$country" }, current_members: { $first: "$current_members" }, previous_members: { $first: "$previous_members" }, genres: { $first: "$genres" }, albums: { $push: "$albums" } } } ]).pretty()
步骤拆解:
$unwind:把艺术家文档里的albums数组拆成多条文档,每条对应一个专辑——这样我们才能针对单个专辑去关联歌曲- 第二个
$lookup:把每个专辑里的songsObjectId列表替换成完整的歌曲文档 $group:按艺术家的_id重新分组,用$push把拆分出去的专辑重新拼成数组,其他艺术家信息用$first取就行(因为同一分组里这些信息都一样)
最终效果
执行完查询后,你会得到包含完整层级数据的结果:每个艺术家下面有对应的专辑数组,每个专辑里又包含对应的完整歌曲数组,比如:
{ "_id" : ObjectId("5b0d2b2c7ac4792df69a9942"), "name" : "Dream Theater", "started_in" : 1985, "active" : true, "country" : "US", "current_members" : [ ObjectId("5b0d2a7c7ac4792df69a9941") ], "previous_members" : [ ObjectId("5b0d2bf57ac4792df69a9954") ], "genres" : [ "prog metal", "prog rock" ], "albums" : [ { "_id" : ObjectId("5b0d16ee7ac4792df69a9924"), "title" : "Images and words", "released" : ISODate("1992-07-07T00:00:00.000Z"), "songs" : [ { "_id" : ObjectId("5b0d15ab7ac4792df69a9916"), "title" : "Pull me under" }, { "_id" : ObjectId("5b0d15ee7ac4792df69a991e"), "title" : "Another day" }, // 剩余歌曲省略 ], "type" : "LP" }, // 另一张专辑省略 ] }
内容的提问来源于stack exchange,提问作者vicusbass
相关产品推荐
相关产品推荐

