Home > other >  How to perform $match after $lookup in mongoDB
How to perform $match after $lookup in mongoDB

Time:09-16

Here I have two collections

Category

[
    {
      id: 1,
      name: "MEN",
      status: true
    },
    {
      id: 2,
      name: "WOMEN",
      staus: true
    }
  ]

Service

[
    {
      name: "service a",
      category_id: 1,
      status: true
    },
    {
      name: "service b",
      category_id: 1,
      status: true
    },
    {
      name: "service c",
      category_id: 1,
      status: true
    },
    {
      name: "service d",
      category_id: 1,
      status: false
    }
  ]

I want all services in the category wise. for that, I have used lookup aggression. I have added one more stage in aggression to filter the service with status true.

I have tried with the following.

db.category.aggregate([
  {
    "$lookup": {
      "from": "services",
      "localField": "id",
      "foreignField": "category_id",
      "as": "services"
    }
  },
  {
    "$match": {
      "services.status": true
    }
  }
])

Expected Output

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "id": 1,
    "name": "MEN",
    "services": [
      {
        "_id": ObjectId("5a934e000102030405000002"),
        "category_id": 1,
        "name": "service a",
        "status": true
      },
      {
        "_id": ObjectId("5a934e000102030405000003"),
        "category_id": 1,
        "name": "service b",
        "status": true
      },
      {
        "_id": ObjectId("5a934e000102030405000004"),
        "category_id": 1,
        "name": "service c",
        "status": true
      },
    ],
    "status": true
  }
]

Have a look at https://mongoplayground.net/p/FXNakB7cLse.

CodePudding user response:

You have multiple options.

With your data you can simply use $filter in a project stage like this:

db.category.aggregate([
  {
    "$lookup": {
      "from": "services",
      "localField": "id",
      "foreignField": "category_id",
      "as": "services"
    }
  },
  {
    "$project": {
      "id": 1,
      "name": 1,
      "status": 1,
      "services": {
        "$filter": {
          "input": "$services",
          "as": "s",
          "cond": {
            "$eq": [
              "$$s.status",
              true
            ]
          }
        }
      }
    }
  }
])

Example here

Other options is to do the $lookup with the condition:

db.category.aggregate([
  {
    "$lookup": {
      "from": "services",
      "let": {
        "id": "$id"
      },
      "pipeline": [
        {
          "$match": {
            "$expr": {
              "$and": [
                {
                  "$eq": [
                    "$category_id",
                    "$$id"
                  ]
                },
                {
                  "$eq": [
                    "$status",
                    true
                  ]
                }
              ]
            }
          }
        }
      ],
      "as": "services"
    }
  }
])

Example here

And the last option (I don't recommend do in this way) is using $unwind, $match and $group: example here

Also, if you don't want to get the document where services: [] you can add a $match stage into the first two options:

{
  "$match": {
    "services": {
      "$ne": []
    }
  }
}
  • Related