Filter query on array of embedded documents(3 levels) in mongoDb

Viewed 550

I have a collection of diagrams. Each diagram has several blocks. And each block is having several ports, which in itself has multiple objects as fields. So far i am only able to apply $filter or $group up to level 1 on blocks. I am unable to filter up to ports based on some condition.

The document structure is: Diagram

[
  {
    "_id": "1",
    "blocks": [
      {
        "port": [
          {
            "portType": {
              "function": "input"
            }
          }
        ]
      }
    ]
  }
]

What i am trying to achieve is to get a list of all ports which have input as portType.function from the collection

db.collection.aggregate([{$project:{"blocks.ports":{$filter:{input:"$blocks.ports",as:"ports",cond:{$in:["input","$$ports.portType.function"]}}}}}]);

Expected output : list<Ports> which have only portType.function as input.

Actual output: Returns all the documents which have at least one port as "input" along with all the other ports as well.

2 Answers

you could simply use two $unwind stages to flatten the arrays, followed by a $match stage like this:

db.collection.aggregate([
  {
    "$unwind": "$blocks"
  },
  {
    "$unwind": "$blocks.port"
  },
  {
    "$match": {
      "blocks.port.portType.function": "input"
    }
  }
])

result:

[
  {
    "_id": "1",
    "blocks": {
      "port": {
        "portType": {
          "function": "input"
        }
      }
    }
  }
]

try it online: mongoplayground.net/p/TOUCkCOSE7D

If you want a list of Port objects you should use double $unwind stage, then $match for blocks.port.portType.function and finally extract only the ports using a $project stage

db.collection.aggregate([
  {
    "$unwind": "$blocks"
  },
  {
    "$unwind": "$blocks.port"
  },
  {
    "$match": {
      "blocks.port.portType.function": "input"
    }
  },
  {
    $project: {
      _id: 0,
      portType: "$blocks.port.portType",
    }
  }
])

If your Port object contains other fields you want to extract, like for example Foo, you have just to add those fields in the $project stage like

$project: {
    _id: 0,
    portType: "$blocks.port.portType",
    foo: "$blocks.port.foo"
    ...
}
Related