Reputation: 1245
I have mongodb documents
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"name" : ["Some Name","Another"],
"zipcode" : ["2223"]
}
{
"_id" : ObjectId("4e8ae86d08101908e1000002"),
"name" : ["Another", "Name"],
"zipcode" : ["2224"]
}
{
"_id" : ObjectId("4e8ae86d08101908e1000003"),
"name" : ["Yet", "Another", "Name"],
"zipcode" : ["2225"]
}
I need to find elements where "name array" have " Another ", "Name" values. I tried to use $in, but if there more values, like in
{
"_id" : ObjectId("4e8ae86d08101908e1000003"),
"name" : ["Yet", "Another", "Name"],
"zipcode" : ["2225"]
}
It doesn't return it =(
Upvotes: 2
Views: 14123
Reputation: 151170
The use of $in should be fine (as explained below) as long as your data is correct and the values do not contain any whitespace. But if they are not exactly the same you are going to need to do some $regex matching, also using the $and form:
db.collection.find({
$and: [
{name: {$regex: 'Another'} },
{name: {$regex: 'Name'}}
]
})
If you just want to get the one document that has both of, and only the fields values you want, and in the same order just supply the argument as an array:
db.collection.find({ name: ["Another", "Name" ] })
The usage of $in is to match any of the elements you supply as a list. In this case you would match all of your documents
db.collection.find({ name: {$in: [ "Another", "Name" ] } })
For the $all operator, it's function is to match all of elements contained in your argument list, so if an argument was not present in the array being searched it would not be included. So just the last two would match:
db.collection.find({ name: {$all: [ "Another", "Name" ] } })
{
"_id" : ObjectId("4e8ae86d08101908e1000002"),
"name" : ["Another", "Name"],
"zipcode" : ["2224"]
}
{
"_id" : ObjectId("4e8ae86d08101908e1000003"),
"name" : ["Yet", "Another", "Name"],
"zipcode" : ["2225"]
}
Finally, if you don't know the order of the elements you are matching, while a little contrived, aggregate gives you a way to get out of jail:
db.collection.aggregate([
// Match using $all to reduce the list
{$match: {name: {$all: ["Name","Another"] }}},
// Keep the original document in the _id
{$project:{
_id: {
_id: "$_id",
name: "$name",
zipcode: "$zipcode"
},
name: 1
}},
// Unwind the "name" array
{$unwind: "$name"},
// Count up the entries per _id
{$group: { _id: "$_id", count: {$sum: 1}}},
// Only match *2* as there were two elements we were expecting
{$match: {count: 2} },
// Project back to the original form
{$project: {
_id:0,
_id: "$_id._id",
name: "$_id.name",
zipcode: "$_id.zipcode"
}}
])
And that's all the forms I can think of.
Upvotes: 6