Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongodb filtering deeply nested arrays with aggregation framework

Thank you in advance for looking at this... My document:

{
    "_id" : { "$oid" : "550b2873e9dd90068070c31b" },
    "dateCreated" : { "$date" : 1426794611867 },
    "sections" : [
        {
            "_id" : { "$oid" : "550b2881e9dd90068070c31d" },
            "index" : 0,
            "name" : "Section 2",
            "slides" : [
                {
                    "_id" : { "$oid" : "550b288ce9dd90068070c321" },
                    "index" : 0,
                    "status" : "Unpublished"
                },
                {
                    "_id" : { "$oid" : "55105b87e9dd90068033ba4a" },
                    "index" : 1,
                    "status" : "Published"
                }
            ]
        },
        {
            "_id" : { "$oid" : "550b287ae9dd90068070c31c" },
            "index" : 1,
            "name" : "Section 1",
            "slides" : [
                {
                    "_id": { "$oid": "550b2888e9dd90068070c31f" },
                    "index" : 0,
                    "status": "Unpublished"
                },
                {
                    "_id" : { "$oid" : "550b288be9dd90068070c320" },
                   "index" : 1,
                   "status" : "Unpublished"
                }
            ]
        }
    ]
}

and the desired result where we return only sections with at least one published slide containing all the published slides

{
    "_id" : { "$oid" : "550b2873e9dd90068070c31b" },
    "dateCreated" : { "$date" : 1426794611867 },
    "sections" : [
        {
            "_id" : { "$oid" : "550b2881e9dd90068070c31d" },
            "index" : 0,
            "name" : "Section 2",
            "slides" : [
                {
                    "_id" : { "$oid" : "55105b87e9dd90068033ba4a" },
                    "index" : 1,
                    "status" : "Published"
                }
            ]
        }
    ]
}

So far I have this:

col.aggregate
([
    {$match : {'name': name}},
    {$unwind:'$sections'},
    {$unwind:'$sections.slides'},
    {$match:{'$sections.slides.status': "Published"}},
    {$group:{_id:'$_id', slides:{$push:'$slides'}}}
]) 

I am having a hard time wrapping my head around the grouping, specifically with nesting each slides array in its parent section array. Also, I would like to omit any empty sections.

I have never used the aggregate method before but I believe this is the correct approach. The mongo docs are a bit sparse when it comes to deeply nested arrays.

like image 485
David Weil Avatar asked Feb 06 '26 19:02

David Weil


1 Answers

Read about $redact stage of MongoDB's pipeline. At first iteration I did this:

.aggregate({ 
    $redact: { 
        $cond: { 
            if: { $eq: ["$status", "Unpublished"] }, 
            then: "$$PRUNE", 
            else: "$$DESCEND"
        }
    }
 }, { 
    $redact: { 
        $cond: { 
            if: { $eq: ["$slides", []] }, 
            then: "$$PRUNE", 
            else: "$$DESCEND"
        }
    }
 })

You can try to rewrite it in more elegant way. I'm pretty sure there is a way to make it with single $redact stage, but you have to skim over the manual and find proper operators. Hope it'll help. Good luck.

like image 112
evilive Avatar answered Feb 08 '26 14:02

evilive



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!