Mongoose聚合查询:如何将$lookup返回的author数组转为对象
问题描述
我有一个Project实体(对应projects集合),每个Project属于一个由Project.orgId标识的组织,其作者信息取自users集合。我希望使用Mongoose查询指定组织的项目,编写了如下聚合管道:
Project.aggregate([ { $match: { organization: new mongoose.Types.ObjectId("an-object-id") } }, { $lookup: { from: User.collection.collectionName, localField: "authorId", foreignField: "_id", as: "author", }, /* 尝试将$unset移至此处,但无效。错误信息:Arguments must be aggregate pipeline operators { "$unset": ["__v", "hash"], }, */ }, { $unset: ["author.__v", "author.hash"] } ])
当前管道存在问题:project.author本应是对象,却变成了单元素数组。
预期结果
{ "codename": "Longhorn", "launch": "1970-01-01T00:00:00Z", "author": { "firstName": "John", "lastName": "Smith" } }
实际结果
{ "codename": "Longhorn", "launch": "1970-01-01T00:00:00Z", "author": [ { "firstName": "John", "lastName": "Smith" } ] }
请问正确的聚合管道应该如何编写?
解决方案
方法一:在$lookup子管道中直接处理成单个对象(推荐)
MongoDB 3.6及以上版本支持在$lookup内定义子管道,既能直接将匹配到的用户文档转为单个对象,还能在子阶段就剔除不需要的字段,减少后续数据处理量:
Project.aggregate([ { $match: { organization: new mongoose.Types.ObjectId("an-object-id") } }, { $lookup: { from: User.collection.collectionName, let: { authorId: "$authorId" }, pipeline: [ { $match: { $expr: { $eq: ["$_id", "$$authorId"] } } }, { $unset: ["__v", "hash"] } // 直接在子管道中移除不需要的字段 ], as: "author" } }, { $addFields: { author: { $first: "$author" } // 将数组转为单个对象 } } ])
方法二:用$arrayElemAt转换数组为对象
如果偏好传统$lookup写法,也可以在关联后用$arrayElemAt提取数组的第一个元素转为对象:
Project.aggregate([ { $match: { organization: new mongoose.Types.ObjectId("an-object-id") } }, { $lookup: { from: User.collection.collectionName, localField: "authorId", foreignField: "_id", as: "author", } }, { $addFields: { author: { $arrayElemAt: ["$author", 0] } // 取数组索引0的元素转为对象 } }, { $unset: ["author.__v", "author.hash"] } ])
关于之前的$unset报错说明
聚合管道的每个阶段必须是独立的对象,不能把$lookup和$unset放在同一个阶段的大括号里,这就是你之前收到Arguments must be aggregate pipeline operators错误的原因,每个操作都要单独写一个{}阶段。
内容的提问来源于stack exchange,提问作者starleaf1
相关产品推荐
相关产品推荐

