Reputation: 14379
If I have this schema...
person = {
name : String,
favoriteFoods : Array
}
... where the favoriteFoods
array is populated with strings. How can I find all persons that have "sushi" as their favorite food using mongoose?
I was hoping for something along the lines of:
PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});
(I know that there is no $contains
in mongodb, just explaining what I was expecting to find before knowing the solution)
Upvotes: 731
Views: 892217
Reputation: 8027
For anyone new to MongoDB and isn't familiar with constructing MongoDB JSON query objects inside the find*
methods, you can leverage the mongoose Query Builders. These are great for newcomers or to encourage consistent querying within your team.
Given the following documents:
{
"_id": "65440e1e5d9811753b6be8e1",
"favoriteFoods": [ "corn", "sushi", "pasta" ],
},
{
"_id": "6545612721b99c8cff188a0a",
"favoriteFoods": [ "eggs", "pizza", "rice" ],
}
You can chain the where()
and in()
methods to match the specified documents:
const person = await PersonModel.find().where('favoriteFoods').in(['sushi']);
// Returns
[
{
"_id": "65440e1e5d9811753b6be8e1",
"favoriteFoods": [ "corn", "sushi", "pasta" ],
}
]
const person = await PersonModel.find().where('favoriteFoods').in(['sushi', 'pizza']);
// Returns
[
{
"_id": "65440e1e5d9811753b6be8e1",
"favoriteFoods": [ "corn", "sushi", "pasta" ],
},
{
"_id": "6545612721b99c8cff188a0a",
"favoriteFoods": [ "eggs", "pizza", "rice" ],
}
]
A full list of Query Builder methods can be found here.
Upvotes: 1
Reputation: 1068
In case You are searching in an Array of objects, you can use $elemMatch
. For example:
PersonModel.find({ favoriteFoods : { $elemMatch: { name: "sushiOrAnytthing" }}});
Upvotes: 2
Reputation: 2185
With populate & $in this code will be useful.
ServiceCategory.find().populate({
path: "services",
match: { zipCodes: {$in: "10400"}},
populate: [
{
path: "offers",
},
],
});
Upvotes: 1
Reputation: 497
There are some ways to achieve this. First one is by $elemMatch
operator:
const docs = await Documents.find({category: { $elemMatch: {$eq: 'yourCategory'} }});
// you may need to convert 'yourCategory' to ObjectId
Second one is by $in
or $all
operators:
const docs = await Documents.find({category: { $in: [yourCategory] }});
or
const docs = await Documents.find({category: { $all: [yourCategory] }});
// you can give more categories with these two approaches
//and again you may need to convert yourCategory to ObjectId
$in
is like OR and $all
like AND. For further details check this link : https://docs.mongodb.com/manual/reference/operator/query/all/
Third one is by aggregate()
function:
const docs = await Documents.aggregate([
{ $unwind: '$category' },
{ $match: { 'category': mongoose.Types.ObjectId(yourCategory) } }
]};
with aggregate() you get only one category id in your category array.
I get this code snippets from my projects where I had to find docs with specific category/categories, so you can easily customize it according to your needs.
Upvotes: 9
Reputation: 311835
As favouriteFoods
is a simple array of strings, you can just query that field directly:
PersonModel.find({ favouriteFoods: "sushi" }, ...); // favouriteFoods contains "sushi"
But I'd also recommend making the string array explicit in your schema:
person = {
name : String,
favouriteFoods : [String]
}
The relevant documentation can be found here: https://docs.mongodb.com/manual/tutorial/query-arrays/
Upvotes: 1017
Reputation: 402
Incase of lookup_food_array is array.
match_stage["favoriteFoods"] = {'$elemMatch': {'$in': lookup_food_array}}
Incase of lookup_food_array is string.
match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}
Upvotes: 10
Reputation: 1
If you'd want to use something like a "contains" operator through javascript, you can always use a Regular expression for that...
eg. Say you want to retrieve a customer having "Bartolomew" as name
async function getBartolomew() {
const custStartWith_Bart = await Customers.find({name: /^Bart/ }); // Starts with Bart
const custEndWith_lomew = await Customers.find({name: /lomew$/ }); // Ends with lomew
const custContains_rtol = await Customers.find({name: /.*rtol.*/ }); // Contains rtol
console.log(custStartWith_Bart);
console.log(custEndWith_lomew);
console.log(custContains_rtol);
}
Upvotes: -8
Reputation: 15693
Though agree with find() is most effective in your usecase. Still there is $match of aggregation framework, to ease the query of a big number of entries and generate a low number of results that hold value to you especially for grouping and creating new files.
PersonModel.aggregate([ { "$match": { $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ] } }, { $project : {"_id": 0, "name" : 1} } ]);
Upvotes: 5
Reputation: 867
For Loopback3 all the examples given did not work for me, or as fast as using REST API anyway. But it helped me to figure out the exact answer I needed.
{"where":{"arrayAttribute":{ "all" :[String]}}}
Upvotes: 2
Reputation: 3470
In case that the array contains objects for example if favouriteFoods
is an array of objects of the following:
{
name: 'Sushi',
type: 'Japanese'
}
you can use the following query:
PersonModel.find({"favouriteFoods.name": "Sushi"});
Upvotes: 126
Reputation: 2793
I feel like $all
would be more appropriate in this situation. If you are looking for person that is into sushi you do :
PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})
As you might want to filter more your search, like so :
PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})
$in
is like OR and $all
like AND. Check this : https://docs.mongodb.com/manual/reference/operator/query/all/
Upvotes: 157
Reputation: 452
In case you need to find documents which contain NULL elements inside an array of sub-documents, I've found this query which works pretty well:
db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})
This query is taken from this post: MongoDb query array with null values
It was a great find and it works much better than my own initial and wrong version (which turned out to work fine only for arrays with one element):
.find({
'MyArrayOfSubDocuments': { $not: { $size: 0 } },
'MyArrayOfSubDocuments._id': { $exists: false }
})
Upvotes: 35
Reputation: 53
I know this topic is old, but for future people who could wonder the same question, another incredibly inefficient solution could be to do:
PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});
This avoids all optimisations by MongoDB so do not use in production code.
Upvotes: -26
Reputation: 3293
There is no $contains
operator in mongodb.
You can use the answer from JohnnyHK as that works. The closest analogy to contains that mongo has is $in
, using this your query would look like:
PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
Upvotes: 244