Home > Blockchain >  Boost search score from data in another collection
Boost search score from data in another collection

Time:04-26

I use Atlas Search to return a list of documents (using Mongoose):

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

These resources can be up and downvoted by users (like questions on Stackoverflow). I want to boost the search score depending on these votes.

I can use the boost operator for that.

Problem: The votes are not a property of the Resource document. Instead, they are stored in a separate collection:

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 },

After I get my search results above, I fetch the votes separately and add them to each search result:

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

I then subtract the downvotes from the upvotes on the client and show the final number in the UI.

How can I incorporate this vote points value into the score of the search results? Do I have to reorder them on the client?

CodePudding user response:

It could be done with one query only, looking similar to:

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

Using the $lookup step to get the votes from the ResourceVote collection

If you want to use the votes to boost the score, you can replace the above $lookup step with something like:

{
    $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
    }
  }

As you can see on this playground example

  • Related