简体   繁体   中英

How to return an array of subdocuments that match a given criteria in Mongodb?

Given a collection like:

{
    "_id": "XXXX",
    "JobId": [
        100
    ],
    "PersonalDetails": [
    {
        "Level": 1,
        "Zone": [
            {
                "Id": 1,
                "Code": "XXXXXXXX",
                "IsAvailable": true
            },
            {
                "Id": 45,
                "Code": "ZZZZZZZZZ",
                "IsAvailable": false
            }
        ]
    }
    ],
    "Timestamp": ISODate("2015-11-01T00:00:00.000Z")
}

I need to get all Zone ids and codes that have the IsAvailable flag set to true.

I have tried the following:

var details = db.test.find(
    {
        JobId: {$in: [100]},
        'PersonalDetails': {$elemMatch: {Zone : {$elemMatch: {IsAvailable: true}}}}
    },
    {
        'PersonalDetails.Zone.Id': 1,
        'PersonalDetails.Zone.Code': 1,
        'PersonalDetails.Zone.IsAvailable': 1
    });

details.forEach(function(doc){
    var myDetails = doc.PersonalDetails;
    myDetails.forEach(function(doc2){
        var myZones = doc2.Zone;
        print(myZones);

This gives me

 {
    "0" : {
            "Id": 1,
            "Code": "XXXXXXXX",
            "IsAvailable": true
    },
    "1" : {
            "Id": 45,
            "Code": "ZZZZZZZZZ",
            "IsAvailable": false
    }
}

But I just want only where the IsAvailable flag is set to true returned.

Am I going about this the wrong way?? I tried using aggregate but ran into the same problem - returning all and not filtering the IsAvailable flag.

You need to use the .aggregate() method.

First of all you need to reduce the size of the documents to process using the $match operator. From there you will need to denormalize your "PersonalDetails" array using the $unwind operator.

You can then use the $project operator to return only sub-documents that match your criteria.

The $map operator in the project stage is used to return array of sub-documents.

db.collection.aggregate([
    { "$match": { 
        "JobId": 100, 
        "PersonalDetails.Zone.IsAvailable": true 
    }}, 
    { "$unwind": "$PersonalDetails" }, 
    { "$project": {
        "zone": {
            "$setDifference": [
                { "$map": { 
                    "input": "$PersonalDetails.Zone", 
                    "as": "z", 
                    "in": { "$cond": [ "$$z.IsAvailable", "$$z", false ] }
                }},
                [false]
            ]
        }
    }}
])

Which returns:

{
        "_id" : "XXXX",
        "zone" : [
                {
                        "Id" : 1,
                        "Code" : "XXXXXXXX",
                        "IsAvailable" : true
                }
        ]
}

Starting from MongoDB 3.2 we can use the $filter operator to do this efficiently

db.collection.aggregate([
    { "$match": { 
        "JobId": 100, 
        "PersonalDetails.Zone.IsAvailable": true 
    }}, 
    { "$unwind": "$PersonalDetails" },
    { "$project": { 
        "zone": {
            "$filter": {
                "input": "$PersonalDetails.Zone", 
                "as": "z", "cond": "$$z.IsAvailable"
            }
        }
    }}
])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM