4

I had a working mongoose query using Aggregate where I filter data based on a value inside an array of objects and am running into trouble due to change in requirements.

This is my how my data looks like in MongoDB (it has been filled with dummy data)

{
"_id" : ObjectId("5ef44528af5a1a2a641cd52b"),
"active" : true,
"name" : "CompanyA",
"contacts" : [ 
    {
        "secondary_emails" : [],
        "_id" : ObjectId("5f19727495f4da29403923e3"),
        "email" : "[email protected]",
        "designation" : "VP Cloud & Security",
        "address" : "",
        "name" : "Ron"
    }
],
"services" : [ 
    {
        "active" : true,
        "_id" : ObjectId("5ef44528af5a1a2a641cd52d"),
        "name" : "Company Management",
        "title" : "Company Helpline",
        "description" : "Offering voice, meetings, collaboration and contact center all on one cloud platform.",
        "categories" : [ 
            "SD-WAN", 
            "Security and Compliance"
        ],
        "sub_categories" : [ 
            "Solution"
        ],
    },
    {
      "active" : true,
      "_id" : ObjectId("5ef44528af5a1a2a641cd52c"),
      "name" : "Company HR",
      "title" : "Human Resources",
      "description" : "Offering HR Services to all",
      "categories" : [ 
          "HR", "Company"
      ],
      "sub_categories" : [ 
          "Solution"
      ],
  }
]}

Objective: Filter services on the basis of the category provided by the user (assuming its "SD-WAN"), before the category was singular and now its an array of values, since one service can belong to multiple categories

Previous Solution My mongoose query, that I was using for singular category values was as below:

db.Collection("suppliers").aggregate([
  {
    $project: {
      services: {
        $filter: {
          input: "$services",
          as: "services",
          cond: { $eq: ["$$services.category", "SD-WAN" ] },
        },
      },
    },
  },
  { $match: { services: { $exists: true, $not: { $size: 0 } } } },
]);

I am having trouble trying to search for SD-WAN inside $$services.categories which is an array of values. Is there any way to update the above query to search for the value in services.categories array using the $eq pipeline operator or some other solution perhaps.

Expected Result

"_id" : ObjectId("5ef44528af5a1a2a641cd52b"),
"services" : [ 
    {
        "active" : true,
        "_id" : ObjectId("5ef44528af5a1a2a641cd52d"),
        "name" : "Company Management",
        "title" : "Company Helpline",
        "description" : "Offering voice, meetings, collaboration and contact center all on one cloud platform.",
        "categories" : [ 
            "SD-WAN", 
            "Security and Compliance"
        ],
        "sub_categories" : [ 
            "Solution"
        ],
    }
]

If anyone can help me out on this, it would be really appreciated. If more information is required, please ask. Thank you.

2 Answers 2

2

Here what you looking for.

[
  {
    $unwind: "$services"
  },
  {
    $match: {
      "services.categories": "SD-WAN"
    }
  },
  {
    $group: {
      _id: "$_id",
      active: {
        $first: "$active"
      },
      name: {
        $first: "$name"
      },
      contacts: {
        $first: "$contacts"
      },
      services: {
        $addToSet: "$services"
      }
    }
  }
]
  1. use $unwind for flatten the array
  2. use $match to filter
  3. use $group to group it back.

We flatten services only. That why I use $addToSet

Working Mongo playground

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the quick response, this resolved my problem perfectly!
what $first do ?
When we unwind, there may be duplicates as much the size of array("Services"). So when we group it, we just need the first object since others are duplicates docs.mongodb.com/manual/reference/operator/aggregation/first
0

Here is another way without using aggregate Playground link : https://mongoplayground.net/p/jmLghGBXb7f

db.collection.find({
  "services": {
    $elemMatch: {
      categories: {
        $regex: "SD-WAN",
        $options: "i"
      }
    }
  }
},
{
  "services": {
    $elemMatch: {
      categories: {
        $regex: "SD-WAN",
        $options: "i"
      }
    }
  }
})

output:

[
  {
    "_id": ObjectId("5ef44528af5a1a2a641cd52b"),
    "services": [
      {
        "_id": ObjectId("5ef44528af5a1a2a641cd52d"),
        "active": true,
        "categories": [
          "SD-WAN",
          "Security and Compliance"
        ],
        "description": "Offering voice, meetings, collaboration and contact center all on one cloud platform.",
        "name": "Company Management",
        "sub_categories": [
          "Solution"
        ],
        "title": "Company Helpline"
      }
    ]
  }
]

1 Comment

This query works partly as it will only return the first service it finds with the category SD-WAN, there can be multiple services with the same category. The query should filter the services that have that category. Is there any way to do that using find?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.