Mongoose对嵌套字段使用$elemMatch投影报错如何解决?
问题原因
你遇到的Cannot use $elemMatch projection on a nested field报错,是因为MongoDB原生的find查询投影仅支持对顶层数组使用$elemMatch,不支持嵌套层级的数组字段(也就是你这里的lists.watched_movies)。无需修改Schema结构,使用聚合管道即可实现需求。
实现代码
const mongoose = require('mongoose'); router.route("/:entryId").get(async (req, res) => { try { // 注意:如果watched_movies的_id是MongoDB默认的ObjectId类型,需要先做类型转换 const targetEntryId = new mongoose.Types.ObjectId(req.params.entryId); const result = await User.aggregate([ // 第一步:先匹配指定邮箱的用户,缩小查询范围 { $match: { email: "some.email@gmail.com" } }, // 第二步:自定义投影,过滤两个数组的匹配项 { $project: { email: 1, // 过滤顶层movies数组,保留title匹配的元素 movies: { $filter: { input: "$movies", cond: { $eq: ["$$this.title", req.body.title] } } }, // 构造lists结构,过滤嵌套的watched_movies数组 lists: { watched_movies: { $filter: { input: "$lists.watched_movies", cond: { $eq: ["$$this._id", targetEntryId] } } } } } } ]); // aggregate返回数组,取第一个元素就是目标文档 res.send(result[0] || {}); console.log(result[0]); } catch (err) { console.log(err); } });
注意事项
- 如果你Schema里
watched_movies的_id是自定义的Number类型,不需要做ObjectId转换,直接使用req.params.entryId即可 - 用
$filter替代$elemMatch可以支持任意层级的数组过滤,返回的格式和你预期的完全一致 - 额外提示:GET请求按HTTP规范不建议携带请求体,你可以把title参数放到URL查询参数(比如
?title=xxx)里,通过req.query.title获取更符合规范
内容的提问来源于stack exchange,提问作者Alex Braga
相关产品推荐
相关产品推荐

