Search code examples
node.jsmongodbmongoosemongodb-lookup

How to get associated document who satisfy match object


In the following query, I want to fetch all verified students and their related meetings. but Trying to fetch all upcoming meetings which means meetings having at least one future date time. My writtten query fetches all verified students but also fetches all the meetings of a student including past one. I want a student to be fetched only if it has any upcoming meetings and fetch only upcoming meetings in returning association. And I want to sort client array of objects according to nearest meeting Any help will be greatly appreciated

     let aggregateDataQuery = [
        {
          $lookup: {
            from: 'meetup',
            localField: '_id',
            foreignField: 'studentId',
            as: 'meetup',
          },
        },
        {
          $project: {
            firstName: 1,
            lastName: 1,
            email: 1,
            meetup: {
              _id: 1,
              startTime: 1,
            },
          },
        },
      ];

      const [result, err] = await of(
        Student.aggregate([
          ...aggregateDataQuery,
          { $match: { 
          verified: true,
              'meetup.startTime': { '$gte': 2021-09-10T08:41:15.746Z }
           } 
          },
          {
            $facet: {
              data: [
                { $sort: sortBy },
                { $skip: skip },
                { $limit: recordLimit },
              ],
              count: [
                {
                  $count: 'count',
                },
              ],
            },
          },
        ])
      );

Solution

  • Depending on the scale of the two collections I would suggest starting the query from the meetings collection as you can utilize an index on the meeting time better that way ( imagine there are no future meeting, in the current approach you still match and lookup the entire student collection ).

    However the current approach you have is fine and just requires a single minor tweak, we need to filter out the "older" meetings, to do so let's use the join condition lookup syntax.

    Now you're new lookup stage will look like:

    {
      "$lookup": {
        "from": "meetup",
        "let": {
          studentId: "$_id"
        },
        "pipeline": [
          {
            $match: {
              $expr: {
                $and: [
                  {
                    $eq: [
                      "$$studentId",
                      "$studentId"
                    ],
                    
                  },
                  {
                    $gt: [
                      "$startTime",
                      "$$NOW"
                    ],
                    
                  }
                ]
              }
            }
          },
          {
            $project: {
              _id: 1,
              startTime: 1,
              
            }
          }
        ],
        "as": "meetup"
      }
    }
    

    Mongo Playground

    And the behavior you require will work as intended with the rest of your pipeline.

    You could just change the $match query to:

    { 
      $match: { 
         verified: true,
         'meetup.0': {$exists: true}
      } 
    }