13

I have a schema like this

{
    members: [{
         type: Schema.Types.ObjectId,
         ref: 'User'
    }]
    createdAt: {
        type: Date,
        default: Date.now
    }
    ...
}

And the docs are

1-

{
    members:
    ["some id 1", "some id 2"]
},
createdAt: "someTime ago"

2-

{
    members:
    ["some id 1", "some id 2", "some id 3"]
},
createdAt: "someTime ago"

I want to find the docs which match the exact an array of elements

['some id 1', 'some id 2']

ie doc 1 in the above sample docs

How this could be possible?
Any help would be appreciated.

1 Answer 1

35

As long as you do realize that you are matching on the ObjectId and not actually anything in the referenced collection you can use the $in operator:

db.collection.find({ "members": { "$in": [ "some id 1", "some id 2" ] } })

Where of course those are your actual ObjectId values.

But if you really mean a document that has exactly that array, then you just pass in the array:

db.collection.find({ "members": [ "some id 1", "some id 2" ] })

And if it must have both the elements but could have others then currently you need to use an $and expression:

db.collection.find({ "$and": [ 
    { "members": "some id 1" },
    { "members": "some id 2" } 
]})

But from release 2.6 an on-wards you can properly use the $all operator to effectively do the same:

db.collection.find({ "members": { "$all": [ "some id 1", "some id 2" ] } })

The other form is matching those two elements only, but in any order. So there are two approaches:

db.collection.find({ "$or": [
    { "members": [ "some id 1", "some id 2" ] },
    { "members": [ "some id 2", "some id 1" ] }
]})

This uses a logical $or to say that the array must be exact but can be arranged either way. And the other approach:

db.collection.find({ "$and": [ 
    { "members": "some id 1" },
    { "members": "some id 2" }
    { "members": { "$size": 2 } }
]})

So this would use $size in order to make sure that when the array contained both of the elements that matched that is also only had just two elements. Which is a nicer syntax than using $or, especially for larger arrays.

And in the future releases as mentioned this gets even cleaner:

db.collection.find({ "$and": [ 
    { "members": { "$all": [ "some id 1", "some id 2" ] } },
    { "members": { "$size": 2 } }
]})

That fairly much covers every interpretation

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your answer. I shall check it
This query return all the docs (doc 1 and doc2) containing the those ids. Not the specific doc( ie in the above example, doc1)
@muhammedbasil Not sure I fully understand which option you want you possibly want an exact match or the use of $and as shown in the edit.
Got it :-), Thanks a lot @Neil Lunn, I made a small modification to $and method, added the $size. And now it's returning the exact one I wanted...
@muhammedbasil I see what you were aiming for now, given that last comment. An alternate way to do that could be to use the second form as an exact match on the array, but wrapped in an $or condition with the other argument in the reverse order. But yes, adding $size to the $and to make sure there are only 2 elements is a much nicer way to do it. I may yet add both of those just for future reference for people.

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.