klaurtar1
klaurtar1

Reputation: 778

Get fields that MongoDB aggregate doesn't match

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 have an aggregation query set up that allows me to find all users who match a query by atleast 25%. Meaning if I search for someone with React skills, SQL skills, and C++ skills it will pull back anyone matching at least one of those. My question at this point is how can I modify my aggregation query to create a new field on the document that lists what fields did not match on the query? So this field would show Mark as missing React and SQL skills if he only has C++ experience.

Here is my current query that shows matching users and the percentage they match to the query I search for (in this case greater than 3 years SQL experience, and greater than 2 years GIT experience):

await User.aggregate([
{
  $addFields: {
    matchingSkills: {
      $filter: {
        input: '$skills',
        cond: {
          $or: [
            {
              $and: [
                { $gt: ['$$this.yearsExperience', 3] },
                { $eq: ['$$this.skill', 'SQL'] },
              ],
            },
          {
            $and: [
              { $gt: [ "$$this.yearsExperience", 2] },
              { $eq: [ "$$this.skill", "Git"] }
            ]
          }
          ],
        },
      },
    },
  },
},
{
  $addFields: {
    matchingSkills: '$$REMOVE',
    percentageMatch: {
      $multiply: [
        { $divide: [{ $size: '$matchingSkills' }, skillSearch.length] }, 
        100,
      ],
    },
  },
},
{
  $match: { percentageMatch: { $gte: 25 } },
},
]);

Upvotes: 0

Views: 540

Answers (1)

Asma Mubeen
Asma Mubeen

Reputation: 51

To get a list of missing skills,

  1. Add field requiredSkills which will be an array of just the searched skills names.

let requiredSkillsStage = {
  $addFields: {
    matchingSkills: {
      $filter: {
        input: "$skills",
        cond: {
          $or: [{
              $and: [{
                  $gt: ["$$this.yearsExperience", 3]
                },
                {
                  $eq: ["$$this.skill", "SQL"]
                }
              ]
            },
            {
              $and: [{
                  $gt: ["$$this.yearsExperience", 2]
                },
                {
                  $eq: ["$$this.skill", "Git"]
                }
              ]
            }
          ]
        }
      }
    },
    requiredSkills: ["SQL", "Git"]
  }
}

  1. Use $map to get matched skills' names.

let matchingSkillsNamesStage = {
  $addFields: {
    "matchingSkillsNames": {
      $map: {
        input: "$matchingSkills",
        as: "matchingSkill",
        in: "$$matchingSkill.skill"
      }
    }
  }
}

  1. Filter-out matched skills from requiredSkills

let missingSkillsStage = {
  $addFields: {
    "missingSkills": {
      $filter: {
        input: "$requiredSkills",
        cond: {
          $not: {
            $in: [
              "$$this",
              "$matchingSkillsNames"
            ]
          }
        }
      }
    }
  }
}

Try it out to see how the output would look like

Upvotes: 1

Related Questions