Search code examples
jsonnode.jsmongodbmongoosesubdocument

MongoDB filter multi sub-documents


I Have a document structured like this in mongo DB, and I want to filter to show only active subdocuments: active cars and active fruits.

    {
       "name":"Andre",
       "fruits":[
           {
              "active":true,
              "fruitname":"apple" 
           },{
              "active":false,
              "fruitname":"banana" 
           }
       ],
       "cars":[
           {
               "active":false,
               "carname":"ford"
           },{
               "active":true,
               "carname":"GM"
           },
       ]
    }

this is my desired result.

    {
       "name":"Andre",
       "fruits":[
           {
              "active":true,
              "fruitname":"apple" 
           }
       ],
       "cars":[
          {
               "active":true,
               "carname":"GM"
           },
       ]
    }

I've tried Aggregate but when any cars or any fruits active, it's return nothing.

    m_object.aggregate([
        { $match : {
           "name": "andre"
       }},
       { $unwind : "$fruits" },
       { $unwind : "$cars" },
       { $match : {
        'fruits.active':{$eq: true}
        }},
       { $match : {
        'cars.active':{$eq: true}
        }},

    { $group : {
       "name": "$name",
       cars: { $addToSet : "$cars" }
       fruits: { $addToSet : "$fruits" }
    }}
    ], function (err, result) {
        if (err) {
            console.log(err);
            return;
        }
        console.log('result');
    });

Is there any way to omit the "active":false subdocuments in those subdocuments?


Solution

  • Use the following aggregation pipeline to get the desired result:

    var pipeline = [
        {
            "$match": {
                "name": "Andre",
                "fruits.active": true,
                "cars.active": true
            }
        },
        { "$unwind": "$fruits" },
        { "$unwind": "$cars" },
        {
            "$match": {            
                "fruits.active": true,
                "cars.active": true
            }
        },
        { 
            "$group": {
                "_id": {
                    "_id": "$_id",
                    "name": "$name"
                },
                "cars": { "$addToSet" : "$cars" },
                "fruits": { "$addToSet" : "$fruits" }
            }
        },
        {
            "$project": {
                "_id": 0,
                "name": "$_id.name",
                "cars": 1,
                "fruits": 1
            }
        }    
    ]
    
    m_object.aggregate(pipeline)
            .exec(function (err, result) {
                if (err) {
                    console.log(err);
                    return;
                }
                console.log('result');
            });
    

    Or you can use the aggregation pipeline builder as follows:

    m_object.aggregate()
            .match({
                "name": "Andre",
                "fruits.active": true,
                "cars.active": true
             })
            .unwind("fruits")
            .unwind("cars")
            .match({
                "fruits.active": true,
                "cars.active": true
             })
            .group({
                "_id": {
                    "_id": "$_id",
                    "name": "$name"
                },
                "cars": { "$addToSet" : "$cars" },
                "fruits": { "$addToSet" : "$fruits" }
            })
            .project({
                "_id": 0,
                "name": "$_id.name",
                "cars": 1,
                "fruits": 1
            })
            .exec(callback);