Xeos
Xeos

Reputation: 6497

MongoDB find projection, matching elements with $ne

I have a query that returns objects containing an array of objects. Within that array of objects, there are some that should not be processed. Here is an object simmilar to what I have:

{
    _id: 12345,
    data: [
        {
            state: 1,
            info: "abc"
        },{
            state: 2,
            info: "cde"
        },{
            state: 2,
            info: "efg"
        }
    ]
}

I want to show only the objects where state does not equal to 1. So I want to get back something like this:

{
    _id: 12345,
    data: [
        {
            state: 2,
            info: "cde"
        },{
            state: 2,
            info: "efg"
        }
    ]
}

There can be hundreds of "main" objects with tens of "sub" objects. I tried using the query:

col.find({'data.info': {$in: [] }, {_id: 1, data: { $elemMatch: { state: {$ne: 1 } } } }, {}, callback);

But that only gives me this:

{
    _id: 12345,
    data: [
        {
            state: 2,
            info: "cde"
        }
    ]
}

In other words, $elemMatch does what it is supposed to do, but I need to get a different result. So is there a way to do that in one query or without pre-processing results (removing entries before any further code reads the data)?

Upvotes: 0

Views: 653

Answers (1)

Stennie
Stennie

Reputation: 65443

The $elemMatch projection operator only returns the first matching element in an array.

To filter the whole array, the best approach in MongoDB 2.2+ would be using the Aggregation Framework. Alternative, you could also do this using Map/Reduce or in your application code.

Example aggregation:

db.data.aggregate(

    // Initial match to limit number of documents
    { $match : {
        data: { $elemMatch: { state: {$ne: 1 } } }
    }},

    // Convert the data array into a stream of documents
    { $unwind: "$data" },

    // Limit to matching elements of the data array
    { $match : {
        "data.state": {$ne: 1 }
    }},

    // Re-group by original document _id
    { $group: {
        _id: "$_id",
        data: { $push: "$data" }
    }}
)

Sample output:

{
    "_id" : 12345,
    "data" : [
        {
            "state" : 2,
            "info" : "cde"
        },
        {
            "state" : 2,
            "info" : "efg"
        }
    ]
}

Upvotes: 1

Related Questions