从另一个集合中的数据提高搜索分数

Boost search score from data in another collection

我使用 Atlas Search return 文档列表(使用 Mongoose):

const searchResults = await Resource.aggregate()
   .search({
       text: {
           query: searchQuery,
           path: ["title", "tags", "link", "creatorName"], 
       },
   }
   )
   .match({ approved: true })
   .addFields({
       score: { $meta: "searchScore" }
   })
   .exec();

这些资源可以被用户投票(比如 Whosebug 上的问题)。我想根据这些投票提高搜索分数

我可以为此使用 boost 运算符。

问题:投票不是 Resource 文档的 属性。相反,它们存储在一个单独的集合中:

const resourceVoteSchema = mongoose.Schema({
    _id: { type: String },
    userId: { type: mongoose.Types.ObjectId, required: true },
    resourceId: { type: mongoose.Types.ObjectId, required: true },
    upDown: { type: String, required: true },

在我得到上面的搜索结果后,我分别取票并将它们添加到每个搜索结果中:

for (const resource of searchResults) {
    const resourceVotes = await ResourceVote.find({ resourceId: resource._id }).exec();
    resource.votes = resourceVotes
}

然后我从客户端的赞成票中减去反对票,并在 UI 中显示最终数字。

如何将这个投票点值合并到搜索结果的分数中?我必须在客户端重新订购吗?

编辑:

这是我更新后的代码。唯一缺少的部分是 让资源投票提高搜索分数,同时将所有 resource-votes 文档保留在 votes 字段中 这样我就可以稍后访问它们。我使用的是 Mongoose 语法,但使用普通 MongoDB 语法的答案对我有用:

const searchResults = await Resource.aggregate()
            .search({
                compound: {
                    should: [
                        {
                            wildcard: {
                                query: queryStringSegmented,
                                path: ["title", "link", "creatorName"],
                                allowAnalyzedField: true,
                            }
                        },
                        {
                            wildcard: {
                                query: queryStringSegmented,
                                path: ["topics"],
                                allowAnalyzedField: true,
                                score: { boost: { value: 2 } },
                            }
                        }
                        ,
                        {
                            wildcard: {
                                query: queryStringSegmented,
                                path: ["description"],
                                allowAnalyzedField: true,
                                score: { boost: { value: .2 } },
                            }
                        }
                    ]
                }
            }
            )
            .lookup({
                from: "resourcevotes",
                localField: "_id",
                foreignField: "resourceId",
                as: "votes",
            })
            .addFields({
                searchScore: { $meta: "searchScore" },
            })
            .facet({
                approved: [
                    { $match: matchFilter },
                    { $skip: (page - 1) * pageSize },
                    { $limit: pageSize },
                ],
                resultCount: [
                    { $match: matchFilter },
                    { $group: { _id: null, count: { $sum: 1 } } }
                ],
                uniqueLanguages: [{ $group: { _id: null, all: { $addToSet: "$language" } } }],
            })
            .exec();

只需一个查询就可以完成,看起来类似于:

Resource.aggregate([
  {
    $search: {
      text: {
        query: "searchQuery",
        path: ["title", "tags", "link", "creatorName"]
      }
    }
  },
  {$match: {approved: true}},
  {$addFields: {score: {$meta: "searchScore"}}},
  {
    $lookup: {
      from: "ResourceVote",
      localField: "_id",
      foreignField: "resourceId",
      as: "votes"
    }
  }
])

使用 $lookup 步骤从 ResourceVote 集合中获取选票

如果您想使用投票来提高分数,您可以将上述 $lookup 步骤替换为:

{
    $lookup: {
      from: "resourceVote",
      let: {resourceId: "$_id"},
      pipeline: [
        {
          $match: {$expr: {$eq: ["$resourceId", "$$resourceId"]}}
        },
        {
          $group: {
            _id: 0,
            sum: {$sum: {$cond: [{$eq: ["$upDown", "up"]}, 1, -1]}}
          }
        }
      ],
      as: "votes"
    }
  },
  {$addFields: { votes: {$arrayElemAt: ["$votes", 0]}}},
  {
    $project: {
      "wScore": {
        $ifNull: [
          {$multiply: ["$score", "$votes.sum"]},
          "$score"
        ]
      },
      createdAt: 1,
      score: 1
    }
  }

如您所见this playground example

编辑:如果你想保留对结果的投票,你可以这样做:

db.searchResults.aggregate([
   {
    $lookup: {
      from: "ResourceVote",
      localField: "_id",
      foreignField: "resourceId",
      as: "votes"
    }
  },
  {
    "$addFields": {
      "votesCount": {
        $reduce: {
          input: "$votes",
          initialValue: 0,
          in: {$add: ["$$value", {$cond: [{$eq: ["$$this.upDown", "up"]}, 1, -1]}]}
        }
      }
    }
  },
  {
    $addFields: {
      "wScore": {
        $add: [{$multiply: ["$votesCount", 0.1]}, "$score"]
      }
    }
  }
])

可以看出here