Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Mongo function for filtering all nested/subdocuments based on a field?

I have some documents in MongoDB that have other nested documents all with an "active" field. For instance :

{
    "id": "PRODUCT1",
    "name": "Product 1",
    "active": true,
    "categories": [
        {
            "id": "CAT-1",
            "active": true,
            "subcategories": [
                {
                    "id": "SUBCAT-1",
                    "active": false
                },
                {
                    "id": "SUBCAT-2",
                    "active": true
                }
            ]
        },
        {
            "id": "CAT-2",
            "active": false,
            "subcategories": [
                {
                    "id": "SUBCAT-3",
                    "active": true
                }
            ]
        }
    ]
}

Is there a way to find all documents but only keep the "active" nested documents.

This is the result I'd like :

{
    "id": "PRODUCT1",
    "name": "Product 1",
    "active": true,
    "categories": [
        {
            "id": "CAT-1",
            "active": true,
            "subcategories": [
                {
                    "id": "SUBCAT-2",
                    "active": true
                }
            ]
        }
    ]
}

Knowing that I do NOT know the document schema beforehand. That's why I need a sort of conditioned wildcard projection... (ie *.active=true). Is this possible or this HAS to be done serverside ?

like image 620
Simo L. Avatar asked Oct 26 '25 02:10

Simo L.


2 Answers

Use $redact.

db.collection.aggregate(
   [
     { $redact: {
        $cond: {
           if: { $eq:["$active", true] },
           then: "$$DESCEND",
           else: "$$PRUNE"
         }
       }
     }
   ]
);

https://mongoplayground.net/p/7UMphkH5OWn

like image 127
s7vr Avatar answered Oct 27 '25 15:10

s7vr


//actual code out from mongo shell 4.2 on windows
//sample document as shared in problem statement, query to find the document from //collection
> db.products.find().pretty();
{
        "_id" : ObjectId("5f748ee5377e73757bb7ceac"),
        "id" : "PRODUCT1",
        "name" : "Product 1",
        "active" : true,
        "categories" : [
                {
                        "id" : "CAT-1",
                        "active" : true,
                        "subcategories" : [
                                {
                                        "id" : "SUBCAT-1",
                                        "active" : false
                                },
                                {
                                        "id" : "SUBCAT-2",
                                        "active" : true
                                }
                        ]
                },
                {
                        "id" : "CAT-2",
                        "active" : false,
                        "subcategories" : [
                                {
                                        "id" : "SUBCAT-3",
                                        "active" : true
                                }
                        ]
                }
        ]
}
//verify mongo shell version no. for reference
> db.version();
4.2.6
//using aggregate and $unwind you can query the inner array elements as shown below
> db.products.aggregate([
... {$unwind: "$categories"},
... {$unwind: "$categories.subcategories"},
... {$match:{"active":true,
...          "categories.active":true,
...          "categories.subcategories.active":true}}
... ]).pretty();
{
        "_id" : ObjectId("5f748ee5377e73757bb7ceac"),
        "id" : "PRODUCT1",
        "name" : "Product 1",
        "active" : true,
        "categories" : {
                "id" : "CAT-1",
                "active" : true,
                "subcategories" : {
                        "id" : "SUBCAT-2",
                        "active" : true
                }
        }
}
>
like image 20
Mallik Avatar answered Oct 27 '25 15:10

Mallik