1

I am trying to get subdocuments which are matched my conditions.

My MongoDB query like that:

db.SCSIssue.find(
 { _id: ObjectId("5439a2992ea8cc0f70feef2d") }, 
 { Statuses: { $elemMatch: { StatusID: { $gte : NumberLong(521055087020736513) } } } }
)

My schema like that:

{
    ....
    "_id": ObjectId("5439a2992ea8cc0f70feef2d"),
    ....
    "Statuses": [{
        ....
        "StatusID": NumberLong(525623822633172993),
    },{
        ....
        "StatusID": NumberLong(521055087020736513),
    },{
        ....
        "StatusID": NumberLong(521060802959532033),
    }]
    ....
},
{
    ....
    "_id": ObjectId("543c04662ea8cd11ec4dda5f"),
    ....
    "Statuses": [{
        ....
        "StatusID": NumberLong(535623822633172993),
    },{
        ....
        "StatusID": NumberLong(541055087020736513),
    },{
        ....
        "StatusID": NumberLong(551060802959532033),
    }]
    ....
},
....

Query result:

{
    "_id" : ObjectId("5439a2992ea8cc0f70feef2d"),
    "Statuses" : [{
        ....
        "StatusID": NumberLong(525623822633172993),
        ....
    }]
}

Expected result:

{
    "_id" : ObjectId("5439a2992ea8cc0f70feef2d"),
    "Statuses" : [{
        ....
        "StatusID": NumberLong(521055087020736513),
    },{
        ....
        "StatusID": NumberLong(521060802959532033),
    }]
}

Briefly, I would like to get subdocuments in array (Statuses) which are greater than or equal my condition and same time which are _id's equal to my condition. I have read lots of answer about that. All of them like my question but in my situation its not work expected.

What am I missing? Thank you.

Dreamcatcher
  • 798
  • 13
  • 31

2 Answers2

0

There is nothing wrong with the behavior of $elemMatch. It works as expected. The doc also says:

However, the $elemMatch projection returns only the first matching element from the array.

As a rule of thumb whenever you project an array using $elemMatch, only one of the elements will be projected at most. If none of the elements in the array match, the field won't be projected at all.

So the result you get is correct, only the first item in the array that has matched the condition in $elemMatch will be projected.

{
    "_id" : ObjectId("5439a2992ea8cc0f70feef2d"),
    "Statuses" : [{
        ....
        "StatusID": NumberLong(525623822633172993),
        ....
    }]
}

You may try changing the order of documents in the statuses array, and may get a different matching document if that document appears ahead of the other matching documents in the array.

Refer: $elemMatch

Coming to your requirement, if you want all the matching array elements in your result, you need to perform an aggregation operation.

  • Match those documents having the required _id and those documents which contain the status sub document that we are searching for.
  • unwind the statuses array.
  • Again match the individual unwinded documents.
  • Finally group the matched documents by _id.

The Code:

db.collection.aggregate([
{$match:{ "_id": ObjectId("5439a2992ea8cc0f70feef2d"),
          "Statuses.StatusID":{$gte : NumberLong(525623822633172993)}}},
{$unwind:"$Statuses"},
{$match:{"Statuses.StatusID":{$gte : NumberLong(525623822633172993)}}},
{$group:{"_id":"$_id",statuses:{$push:"$Statuses"}}}
])

which will give you all the matching sub documents in the array.

BatScream
  • 19,260
  • 4
  • 52
  • 68
  • Thank you for answer. I have a one more question about your query. Why do you use "Statuses.StatusID" match two times? Is there any tricks? – Dreamcatcher Nov 24 '14 at 20:44
  • Adding the criteria in the initial match operation stage would prevent the following unnecessary unwind and match operators if the document did not have a status sub document with the matching statusId. – BatScream Nov 24 '14 at 21:09
0

FYI the find method has 2 parameters, the first one is the query you want to send to the database and the second one is the projection. When you do your search you are sending this ...find({query},{projection})

db.SCSIssue.find(
{ _id: ObjectId("5439a2992ea8cc0f70feef2d") }, 
{ Statuses: { $elemMatch: { StatusID: { $gte : NumberLong(521055087020736513)}}}})

What you might want to do is to do all in the query part of the find method.

db.SCSIssue.find(
{ _id: ObjectId("5439a2992ea8cc0f70feef2d"), 
   Statuses: { $elemMatch: { StatusID: { $gte : NumberLong(521055087020736513)}},
   xxxxx <- second param for the projection part
)
1020rpz
  • 914
  • 10
  • 21