Найти документ с массивом, который содержит определенное значение
Если у меня есть эта схема...
person = {
name : String,
favoriteFoods : Array
}
... где favoriteFoods
массив заполняется строками. Как я могу найти всех людей, у которых есть "суши" как их любимая еда, используя мангуста?
Я надеялся на что-то вроде:
PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});
(Знаю что нет $contains
в mongodb, просто объясняя, что я ожидал найти, прежде чем знать решение)
13 ответов
Как favouriteFoods
это простой массив строк, вы можете просто запросить это поле напрямую:
PersonModel.find({ favouriteFoods: "sushi" }, ...);
Но я бы также рекомендовал сделать строковый массив явным в вашей схеме:
person = {
name : String,
favouriteFoods : [String]
}
Здесь нет $contains
оператор в mongodb.
Вы можете использовать ответ от JohnnyHK, как это работает. Наиболее близкая аналогия с тем, что монго имеет $in
с помощью этого ваш запрос будет выглядеть так:
PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
Я чувствую $all
было бы более уместным в этой ситуации. Если вы ищете человека, который любит суши, вы делаете:
PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})
Как вы можете захотеть отфильтровать ваш поиск, например, так:
PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})
$in
это как ИЛИ и $all
как AND Проверьте это: https://docs.mongodb.com/manual/reference/operator/query/all/
В случае, если массив содержит объекты, например, если favouriteFoods
это массив объектов следующего:
{
name: 'Sushi',
type: 'Japanese'
}
Вы можете использовать следующий запрос:
PersonModel.find({"favouriteFoods.name": "Sushi"});
Если вам нужно найти документы, содержащие элементы NULL, внутри массива поддокументов, я нашел этот запрос, который работает довольно хорошо:
db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})
Этот запрос взят из этого поста: массив запросов MongoDb с нулевыми значениями
Это была отличная находка, и она работает намного лучше, чем моя первоначальная и неправильная версия (которая отлично работала только для массивов с одним элементом):
.find({
'MyArrayOfSubDocuments': { $not: { $size: 0 } },
'MyArrayOfSubDocuments._id': { $exists: false }
})
Incase of lookup_food_array это массив.
match_stage["favoriteFoods"] = {'$elemMatch': {'$in': lookup_food_array}}
Incase of lookup_food_array это строка.
match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}
Есть несколько способов добиться этого. Первый - от
$elemMatch
оператор:
const docs = await Documents.find({category: { $elemMatch: {$eq: 'yourCategory'} }});
// you may need to convert 'yourCategory' to ObjectId
Второй - операторы или:
const docs = await Documents.find({category: { $in: [yourCategory] }});
или
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
похоже на ИЛИ и
$all
как И. Для получения дополнительной информации проверьте эту ссылку: https://docs.mongodb.com/manual/reference/operator/query/all/
Третий - от
aggregate()
функция:
const docs = await Documents.aggregate([
{ $unwind: '$category' },
{ $match: { 'category': mongoose.Types.ObjectId(yourCategory) } }
]};
с aggregate() вы получаете только один идентификатор категории в своем массиве категорий.
Я получаю эти фрагменты кода из своих проектов, где мне нужно было найти документы с определенной категорией / категориями, чтобы вы могли легко настроить их в соответствии с вашими потребностями.
Хотя согласитесь с find() наиболее эффективным в вашем случае использования. Тем не менее, существует $ match структуры агрегации, чтобы упростить запрос большого количества записей и генерировать небольшое количество результатов, которые имеют ценность для вас, особенно для группировки и создания новых файлов.
PersonModel.aggregate([ { "$match": { $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ] } }, { $project : {"_id": 0, "name" : 1} } ]);
Если вы ищете в массиве объектов, вы можете использовать$elemMatch
. Например:
PersonModel.find({ favoriteFoods : { $elemMatch: { name: "sushiOrAnytthing" }}});
Для Loopback3 все приведенные примеры не работали для меня или так же быстро, как использование REST API. Но это помогло мне найти точный ответ, который мне был нужен.
{"where":{"arrayAttribute":{ "all" :[String]}}}
С заполнением & $ в этом коде будет полезно.
ServiceCategory.find().populate({
path: "services",
match: { zipCodes: {$in: "10400"}},
populate: [
{
path: "offers",
},
],
});
Если вы хотите использовать что-то вроде оператора "Содержит" через JavaScript, вы всегда можете использовать Регулярное выражение для этого...
например. Допустим, вы хотите получить клиента, у которого в качестве имени указано "Бартоломью"
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);
}
Я знаю, что эта тема старая, но для будущих людей, которые могут задаться тем же вопросом, можно сделать еще одно невероятно неэффективное решение:
PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});
Это позволяет избежать всех оптимизаций MongoDB, поэтому не используйте их в производственном коде.