MongoDB query that shows how many fields document matches

Viewed 113

I am creating a MERN app that allows users to sign up and save their skills to a database. I am creating an admin panel that allows me to search for users by skill. User's skills will look like this in the database:

skills: [
  {skill: 'React', yearsExperience: 3},
  {skill: 'HTML', yearsExperience: 5},
  {skill: 'JavaScript', yearsExperience: 5},
  {skill: 'Git', yearsExperience: 3},
  {skill: 'TypeScript', yearsExperience: 1},
  {skill: 'C++', yearsExperience: 1}
]

I am using OR queries to query the database to pull back users who match several requirements like this (User's with greater than 3 years experience in C, or users with greater than 3 years experience in SQL):

$or: [
    { skills: { $elemMatch: { skill: 'C', yearsExperience: { $gt: 3 } } } },
    { skills: { $elemMatch: { skill: 'SQL', yearsExperience: { $gt: 3 } } } },
 ];

My question is, how can I return the users in an array with a property called percentageMatch that shows how many fields the query searched for that they matched? For example, if Mark has both C and SQL experience with greater than 3 years his property will say 100%, but if James has only 3 years or greater of SQL experience, and no C experience his percentageMatching will say 50%.

// Response of match query
[
  {
    name: 'Mark',
    skills: [...],
    percentageMatch: 100%
  },
  {
    name: 'James',
    skills: [...],
    percentageMatch: 50%
  }
]
1 Answers

You can do it with aggregation,

  • $match to get your desired document
  • $filter to filter the array based on your condition and assign it to matchingSkills using $addFields
  • then get the percentage using $multiply, $divide.

Here is the code

db.collection.aggregate([
  {
    "$match": { name: "mark" }
  },
  {
    $addFields: {
      matchingSkills: {
        $filter: {
          input: "$skills",
          cond: {
            $or: [
              {
                $and: [
                  { $gt: [ "$$this.yearsExperience", 3 ] },
                  { $eq: [ "$$this.skill", "HTML" ]}
                ]
              },
              {
                $and: [
                  { $gt: [ "$$this.yearsExperience", 3] },
                  { $eq: [ "$$this.skill", "Git"] }
                ]
              }
            ]
          }
        }
      }
    }
  },
  {
    $addFields: {
      matchingSkills: "$$REMOVE",
      percentageMatch: {
        $multiply: [
          { $divide: [ { $size: "$matchingSkills" }, 2 ]}, // yu already know how many values you need to pass, thats' why `2`
          100
        ]
      }
    }
  }
])

Working Mongo playground

Related