MongoDB聚合管道查询:获取父文档及其一级子文档
问题描述
我有一个名为things的MongoDB集合,包含以下文档:
[ { "_id": 1, "name": "one", "others": [2, 3] }, { "_id": 2, "name": "two" }, { "_id": 3, "name": "three" }, { "_id": 4, "name": "four" } ]
其中第一个文档通过others数组构成树形结构,数组里是其子文档的_id。我需要编写一个查询,返回扁平的文档列表:第一个是父文档,后面跟着others数组里对应的直接子文档(不需要递归)。比如传入_id=1时,预期结果是:
[ { "_id": 1, "name": "one", "others": [2, 3] }, { "_id": 2, "name": "two" }, { "_id": 3, "name": "three" } ]
我尝试了下面的聚合语句,但报错$in needs an array:
db.things.aggregate([{ $match: { _id: 1 } }, { $match: { _id: { $in: '$others'}} } ])
请问正确的查询语法是什么?
解决方案
你之前的写法错误在于第二个$match阶段无法引用管道前序阶段的字段值——$match是针对整个集合的全局过滤,它会把'$others'当成普通字符串而非数组,所以才会报$in needs an array的错误。
可以通过聚合管道的$lookup+$concatArrays组合实现需求,具体写法如下:
db.things.aggregate([ // 1. 匹配父文档 { $match: { _id: 1 } }, // 2. 关联查询子文档,存入children字段 { $lookup: { from: "things", localField: "others", foreignField: "_id", as: "children" } }, // 3. 把父文档和子数组合并成一个新数组 { $project: { docs: { $concatArrays: [[$$ROOT], "$children"] } } }, // 4. 展开数组为单个文档 { $unwind: "$docs" }, // 5. 将每个数组元素转为根文档,同时移除多余的children字段 { $replaceRoot: { newRoot: { $mergeObjects: [ "$docs", { children: "$$REMOVE" } ] } } } ])
执行逻辑说明:
$match定位到_id=1的父文档;$lookup根据父文档的others数组,从things集合中查询对应的子文档,结果存在children数组里;$concatArrays把父文档(用[$$ROOT]转成单元素数组)和children数组合并成一个新数组docs;$unwind将docs数组拆分成单个文档;$replaceRoot把每个docs元素转为根文档,同时用$$REMOVE去掉临时生成的children字段,恢复原文档结构。
另一种简化写法(如果不需要严格保证父文档在前):
如果你对结果顺序没有强制要求,也可以直接用$or条件一次查询:
// 先获取父文档的others数组,再组合查询条件 const parent = db.things.findOne({ _id: 1 }); db.things.find({ $or: [ { _id: 1 }, { _id: { $in: parent.others } } ] })
这种写法需要两次查询,但代码更简洁,适合对顺序要求不高的场景。
内容的提问来源于stack exchange,提问作者Matthew Adams
相关产品推荐
相关产品推荐

