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