0

If I have a multiple documents in a mongodb collection that look like this:

// document 1
{
    _id: '123',
    date: '5/10/15',

    charges: [{
        amount: 500,
        description: 'foo',
    },{
        amount: 400,
        description: 'bar',
    }],
}


// document 2    
{
    _id: '456',
    date: '5/11/15',

    charges: [{
        amount: 500,
        description: 'foo',
    },{
        amount: 300,
        description: 'foo',
    }],
}

I want to create and array of all charges that have an amount of 500. The result should look like this:

[{
    amount: 500,
    description: 'foo'
}, {
    amount: 500,
    description: 'foo'
}]

What is the most efficient way to accomplish this?

2 Answers 2

2

Try this:

db.collection.aggregate(
    [
        { 
            $unwind: "$charges"
        },
        {
            $match: {
                amount: 500
            }
        }
    ]
);
Sign up to request clarification or add additional context in comments.

Comments

1

Across documents you use the aggregation framework with $unwind and $group:

db.collection.aggregate([
    // Match documents with the required criteria
    { "$match": { "charges.amount": 500 } },

    // Unwind to de-normalize the content
    { "$unwind": "$charges" },

    // Filter the de-normalized documents
    { "$match": { "charges.amount": 500 } },

    // Group back the result
    { "$group": {
        "_id": null,
        "charges": { "$push": "$charges" }
    }}        
])

Or a bit more efficient in modern versions is to filter the array first:

db.collection.aggregate([
    // Match documents with the required criteria
    { "$match": { "charges.amount": 500 } },

    // Pre filter the array
    { "$redact": {
        "$cond": {
            "if": { "$eq": [{ "$ifNull": [ "$amount", 500 ] }, 500 ]},
            "then": "$$DESCEND",
            "else": "$$PRUNE"
        }
    }},

    // Unwind to de-normalize the content
    { "$unwind": "$charges" },

    // Group back the result
    { "$group": {
        "_id": null,
        "charges": { "$push": "$charges" }
    }}        
])

Future versions ( working in current development releases ) will have a more helpful $filter method:

db.collection.aggregate([
    // Match documents with the required criteria
    { "$match": { "charges.amount": 500 } },

    // Filter the array
    { "$project": {
        "charges": {
            "$filter": {
                "input": "$charges",
                "as": "charge",
                "cond": {
                    "$eq": [ "$$charge.amount", 500 ]
                }
            }
        }
    }},

    // Unwind to de-normalize the content
    { "$unwind": "$charges" },

    // Group back the result
    { "$group": {
        "_id": null,
        "charges": { "$push": "$charges" }
    }}        
])

All result in:

{
    "_id": null,
    "charges": [
        {
            amount: 500,
            description: 'foo'
        }, {
            amount: 500,
            description: 'foo'
        }
    ]
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.