I want to make a search function with mongoose, and I have to be able to make a research with multiple fields (with Mongoose, in NodeJS).
So, I do something like this :
const result = await myModel.find({
$or [{condition1: "value1"}, {condition2: "value2"}, etc...]
});
But, I want to sort the result by the number of condition the object returned have. Like :
If I have 2 conditions, I want to display first the objects respecting the 2 conditions, then the objects respecting the 1st condition, and finally the objects respecting the 2nd condition.
Do you guys know how I can do this? :)
Thanks in advance !
================EDIT================
This is the new search function :
/**
* Search function which returns users matching jobs and skills.
*
* @param {Array[String]} jobs
* @param {Array[String]} skills
* @return {Array[UserModel]} users
*/
async search(jobs, skills) {
// Normalized arrays of jobs, skills and fields (to use it in a mongoose request).
const jobSkills = [];
const associatedSkills = [];
const fields = [];
for (const job of jobs) {
jobSkills.push({
$cond: [
{
$eq: ["$jobSkills", job],
},
2,
0,
],
});
fields.push({
jobSkills: job,
});
}
for (const skill of skills) {
associatedSkills.push({
$cond: [
{
$eq: ["$associatedSkills", skill],
},
1,
0,
],
});
fields.push({
associatedSkills: skill,
});
}
// Request to find users matching jobs and skills.
const users = await UserModel.aggregate([
{
$match: {
$or: fields,
},
},
{
$addFields: {
sortField: {
$sum: jobSkills.concat(associatedSkills),
},
},
},
{
$sort: {
sortField: -1,
},
},
]);
return users;
}
Aggregation Log :
Aggregate {
_pipeline: [
{ '$match': [Object] },
{ '$addFields': [Object] },
{ '$sort': [Object] }
],
_model: Model { User },
options: {}
}
In general, a document either matches a query predicate or it doesn't. There isn't really a concept of one document matching "better" than another. So it looks like you'll want to generate a custom value in a new field and sort on that. This will need to be done via an aggregation.
So after the $match
, we'll want an $addFields
stage that effectively duplicates the query predicates. For each one it will be wrapped in a conditional statement ($cond
) where we add 1
for a match or 0
otherwise, e.g.:
{
$cond: [
{
$eq: [
"$condition1",
"value1"
]
},
1,
0
]
}
Then there will be a $sum
pulling them together to generate the final score to sort on.
Taken together, the aggregation will look something like this:
db.collection.aggregate([
{
$match: {
$or: [
{
condition1: "value1"
},
{
condition2: "value2"
}
]
}
},
{
$addFields: {
sortField: {
"$sum": [
{
$cond: [
{
$eq: [
"$condition1",
"value1"
]
},
1,
0
]
},
{
$cond: [
{
$eq: [
"$condition2",
"value2"
]
},
1,
0
]
}
]
}
}
},
{
$sort: {
"sortField": -1
}
}
])