sekitaka
sekitaka

Reputation: 1032

How to search embedded array

I want to get all matching values, using $elemMatch.

// create test data
db.foo.insert({values:[0,1,2,3,4,5,6,7,8,9]})
db.foo.find({},{
    'values':{
        '$elemMatch':{
            '$gt':3
         }
    }
}) ;

My expecected result is {values:[3,4,5,6,7,8,9]} . but , really result is {values:[4]}. I read mongo document , I understand this is specification.

How do I search for multi values ? And more, I use 'skip' and 'limit'.

Any idea ?

Upvotes: 0

Views: 122

Answers (2)

BatScream
BatScream

Reputation: 19700

Using Aggregation:

db.foo.aggregate([
{$unwind:"$values"},
{$match:{"values":{$gt:3}}},
{$group:{"_id":"$_id","values":{$push:"$values"}}}
])

You can add further filter condition in the $match, if you would like to.

You can't achieve this using an $elemMatch operator since, mongoDB doc says:

The $elemMatch projection operator limits the contents of an array field that is included in the query results to contain only the array element that matches the $elemMatch condition.

Note

The elements of the array are documents.

Upvotes: 1

Neil Lunn
Neil Lunn

Reputation: 151170

If you look carefully at the documentation on $elemMatch or the counterpart to query of the positional $ operator then you would see that only the "first" matched element is returned by this type of "projection".

What you are looking for is actually "manipulation" of the document contents where you want to "filter" the content of the array in the document rather than return the original or "matched" element, as there can be only one match.

For true "filtering" you need the aggregation framework, as there is more support there for document manipulation:

db.foo.aggregate([

    // No point selecting documents that do not match your condition
    { "$match": { "values": { "$gt": 3 } } },

    // Unwind the array to de-normalize as documents
    { "$unwind": "$values },

    // Match to "filter" the array
    { "$match": { "values": { "$gt": 3 } } },

    // Group by to the array form
    { "$group": {
        "_id": "$_id",
        "values": { "$push": "$values" }
    }}
])

Or with modern versions of MongoDB from 2.6 and onwards, where the array values are "unique" you could do this:

db.foo.aggregate([
    { "$project": {
        "values": {
            "$setDifference": [
                { "$map": {
                    "input": "$values",
                    "as": "el",
                    "in": {
                        "$cond": [
                            { "$gt": [ "$$el", 3 ] },
                            "$$el",
                            false
                        ]
                    }
                }},
                [false]
            ]
        }
    }}
])

Upvotes: 0

Related Questions