You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
    }
  }
])

核心要点

  1. 跨阶段字段引用:利用$lookup的let参数将目标书籍的genre和_id传递到内部聚合管道,通过$$前缀引用这些变量,解决不同阶段无法直接引用字段的问题。
  2. 相似匹配逻辑:通过$setIntersection计算genre数组的交集,用$size获取交集长度作为匹配度,筛选匹配度高的书籍。
  3. 结果整理:最后通过$project只保留需要的字段,确保输出格式符合预期。

内容的提问来源于stack exchange,提问作者Fallen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 21:20:20