Home > Back-end >  How to compare two array of objects in query MongoDB
How to compare two array of objects in query MongoDB

Time:01-09

I have one model named User.

const userSchema = new Schema({

    technologiesStack: [
       {
         technology: Id
         yearsOfExperience: Number
       }
     ]

});

And Model Job, each job has their field techsRequired

const jobSchema = new Schema({
    techsRequired: [
        {
           technology: Id,
           yearsOfExperience: Number
        }
    
     ]
});

Now, I want to recommend personalized jobs to each User, so i need to get all jobs that match with the User's tech stack.

How can i do that?

Example Code

CodePudding user response:

The current structure makes it quite difficult but at the end of the day it just required multiple filtering of arrays.

Our strategy will be to first match all the jobs that the user satisfy their requirements using $all, which you have already started doing - this step is not required but it's done just to reduce the scale of the matching documents.

The next step will be to iterate over the requirements, matching each one to the user tech stack and making sure the year requirement is met, like so:

const userTechInput = [
    {
        technology: 1,
        yearsOfExperience: 6
    },
    {
        technology: 3,
        yearsOfExperience: 15
    }
]

db.collection.aggregate([
  {
    $match: {
      "technologiesStack.technology": {
        $all: userTechInput.map((v) => v. technology)
      }
    }
  },
  {
    $addFields: {
      matched: {
        $filter: {
          input: "$technologiesStack",
          as: "tech",
          cond: {
            $lte: [
              "$$tech.yearsOfExperience",
              {
                "$getField": {
                  "field": "yearsOfExperience",
                  "input": {
                    "$arrayElemAt": [
                      {
                        $filter: {
                          input: userTechInput,
                          as: "userTech",
                          cond: {
                            "$eq": [
                              "$$userTech.technology",
                              "$$tech.technology"
                            ]
                          }
                        }
                      },
                      0
                    ]
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $match: {
      $expr: {
        $eq: [
          {
            $size: "$matched"
          },
          {
            $size: "$technologiesStack"
          }
        ]
      }
    }
  }
])

Mongo Playground

Notice I used the new $getField operator added in Mongo version 5.0, this is just for connivence and in order to simplify the already complex $addFields stage, it let's you grab a field from an object, in this case the yearsOfExperience field from the input. If you are on an older Mongo version one alternative will be to either use ObjectToArray to convert the input obj to an array, filter it and then comparing "$$v. yearsOfExperience", obviously it makes the code sample much larger.

One last thing I'll recommend, unless this is a toy example, if you're planning to use this code in production I suggest you rethink your structure and methodology. as you can see the current structure is very hard to query, additionally you'd probably want to calculate a matching score instead of an exact match, for example if the requirements are 15 years of nodejs and I have 14 years of it, it will automatically get filtered out, but we know that in the real world things are not quite so black and white.

  •  Tags:  
  • Related