Find document with array that contains a specific value

MongodbMongoose

Mongodb Problem Overview


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)

Mongodb Solutions


Solution 1 - Mongodb

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/

Solution 2 - Mongodb

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"]} }, ...);

Solution 3 - Mongodb

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/

Solution 4 - Mongodb

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"});

Solution 5 - Mongodb

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: https://stackoverflow.com/questions/15335197/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 }
})

Solution 6 - Mongodb

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}

Solution 7 - Mongodb

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} } > ]);

Solution 8 - Mongodb

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]}}}

Solution 9 - Mongodb

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.

Solution 10 - Mongodb

With populate & $in this code will be useful.

ServiceCategory.find().populate({
    path: "services",
    match: { zipCodes: {$in: "10400"}},
    populate: [
        {
            path: "offers",
        },
    ],
});

Solution 11 - Mongodb

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);
}

Solution 12 - Mongodb

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLudwig MagnussonView Question on Stackoverflow
Solution 1 - MongodbJohnnyHKView Answer on Stackoverflow
Solution 2 - MongodbAlistair NelsonView Answer on Stackoverflow
Solution 3 - MongodbPobeView Answer on Stackoverflow
Solution 4 - MongodbKfir ErezView Answer on Stackoverflow
Solution 5 - MongodbJesus CamponView Answer on Stackoverflow
Solution 6 - MongodbgautamView Answer on Stackoverflow
Solution 7 - MongodbAmitesh BhartiView Answer on Stackoverflow
Solution 8 - MongodbMark Ryan OrosaView Answer on Stackoverflow
Solution 9 - MongodbRustamjon KirgizbaevView Answer on Stackoverflow
Solution 10 - MongodbHiran D.A WalawageView Answer on Stackoverflow
Solution 11 - MongodbAlingenomenView Answer on Stackoverflow
Solution 12 - Mongodbuser3027146View Answer on Stackoverflow