MongoDB聚合管道跨阶段字段引用实现书籍相似推荐
MongoDB聚合管道实现书籍详情+相似推荐(解决跨阶段字段引用)
问题概述
给定如下BookSchema:
const BookSchema = new Schema( { title: { type: String, required: true }, author_id: { type: Schema.Types.ObjectId, ref: 'User' }, summary: { type: String, required: true }, isbn: { type: String }, genre: [{ type: String, required: true }], doc: {}, img: { type: String }, review_id: [{ type: Schema.Types.ObjectId, ref: 'Review' }], pub_date: { type: Date, required: true }, totalRating: { type: Number, default: 0}, ratingCount: { type: Number, default: 0 }, hotRank: { type: Number, default: 0 }, popRank: { type: Number, default: 0 } } );
需要实现:通过书籍ID查询目标书籍详情,同时在同一聚合查询中基于genre标签匹配相似书籍,核心难点是聚合管道跨阶段引用文档字段。
已尝试的聚合查询(存在字段引用问题):
db.books.aggregate( [ { $match: { _id: `book_id` } }, { $addFields: { "count": { $size: { $setIntersection: ["$genre", `founded_book_genre_tag`] } } } }, { $sort: { "count": -1 } }, { $project: { _id: 1, title: 1 } } ])
示例数据
[ { "_id": 1,"title": "sample1","genre": ["Sports","War","Fantasy"] }, { "_id": 2,"title": "sample2","genre": ["Fantasy","Games","War"] }, { "_id": 3,"title": "sample3","genre": ["Fantasy","Games","Sports","War"] }, { "_id": 4, "title": "sample4","genre": ["Games", "Fantasy","War","Action","Urban"] }, { "_id": 5, "title": "sample5","genre": ["History","Fantasy","Mystery","War"] } ]
期望输出(查询_id=2的书籍)
{ _id: 2, title: "sample2", genre: ["Fantasy","Games","War"], recd: [ {_id:3}, {_id: 4} ] }
解决方案
通过$lookup的let参数传递目标书籍字段到内部管道,结合聚合操作完成匹配和筛选:
db.books.aggregate([ // 匹配目标书籍,获取其genre和_id { $match: { _id: ObjectId("2") } }, // 替换为实际查询的书籍ID,需转为ObjectId // 关联查询相似书籍,通过let传递目标字段 { $lookup: { from: "books", let: { target_genre: "$genre", target_id: "$_id" }, pipeline: [ // 排除目标书籍本身 { $match: { $expr: { $ne: ["$_id", "$$target_id"] } } }, // 计算与目标书籍的genre交集数量 { $addFields: { match_count: { $size: { $setIntersection: ["$genre", "$$target_genre"] } } } }, // 按匹配数量降序排序 { $sort: { match_count: -1 } }, // 筛选匹配数量>=2的书籍(对应示例中的3和4) { $match: { match_count: { $gte: 2 } } }, // 仅保留_id字段 { $project: { _id: 1 } } ], as: "recd" } }, // 整理输出字段 { $project: { _id: 1, title: 1, genre: 1, recd: 1 } } ])
核心要点
- 跨阶段字段引用:利用
$lookup的let参数将目标书籍的genre和_id传递到内部聚合管道,通过$$前缀引用这些变量,解决不同阶段无法直接引用字段的问题。 - 相似匹配逻辑:通过
$setIntersection计算genre数组的交集,用$size获取交集长度作为匹配度,筛选匹配度高的书籍。 - 结果整理:最后通过
$project只保留需要的字段,确保输出格式符合预期。
内容的提问来源于stack exchange,提问作者Fallen
相关产品推荐
相关产品推荐

