Найти документ с массивом, который содержит определенное значение

499

Если у меня есть эта схема ...

person = {
    name : String,
    favoriteFoods : Array
}

... где favoriteFoodsмассив заполнен строками. Как я могу найти всех людей, у которых есть "суши" как их любимая еда, используя мангуста?

Я надеялся на что-то вроде:

PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});

(Я знаю, что нет $containsв mongodb, просто объясняю, что я ожидал найти, прежде чем знать решение)

Людвиг Магнуссон
источник

Ответы:

693

Как favouriteFoodsи простой массив строк, вы можете просто запросить это поле напрямую:

PersonModel.find({ favouriteFoods: "sushi" }, ...);

Но я бы также рекомендовал сделать строковый массив явным в вашей схеме:

person = {
    name : String,
    favouriteFoods : [String]
}

Соответствующую документацию можно найти здесь: https://docs.mongodb.com/manual/tutorial/query-arrays/

JohnnyHK
источник
19
Это также работает, если favouriteFoods:favouriteFoods:[{type:Schema.Types.ObjectId, ref:'Food'}]
k88074
12
Как кто-то новичок в Mongo, пришедший из СУБД, такой как MySQL, обнаружил, что такие решения работают так просто, без использования JOIN и дополнительных таблиц, и меня удивляет, почему я не начал работать с Mongo раньше. Но это не значит, что одна СУБД превосходит другую - это зависит от вашего варианта использования.
Ирвин Лим
9
Не ошибись. Даже если это список dict, вы все равно можете запросить его таким образом. Образец: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Амина Нураини
3
Что происходит, когда я хочу найти массив, который содержит как минимум две строки?
Аэро Ван
151

В $containsmongodb нет оператора.

Вы можете использовать ответ от JohnnyHK, как это работает. Самая близкая аналогия к содержащемуся в Монго состоит в том $in, что при использовании этого ваш запрос будет выглядеть так:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
Алистер Нельсон
источник
10
Это правильно? Разве mongodb не ожидает массив значений при использовании $ in? как {имя: {$ in: ["Пол", "Дейв", "Ларри", "Адам"]}}?
Людвиг Магнуссон
38
А? Это не нужно. $inиспользуется, когда у вас есть несколько значений запроса, и документ должен соответствовать одному из них. Для обратного (о чем этот вопрос), ответ JohnnyHK является правильным. Я собирался понизить голос, но я думаю, что этот ответ может быть полезным для других людей, которые попадают на эту страницу.
MalcolmOcean
4
Но это помогло мне сделать запрос с несколькими значениями: D Большое спасибо!
Александр Бурлье
11
Спасибо. Это то, что я на самом деле искал, способ поиска нескольких значений:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli
@MalcolmOcean корректно в том смысле, что оператор $ in для обратного, с массивом в качестве значения . Поле является массивом то , что вопрос спрашивает о. Однако, если и поле, и значение являются массивами, то и этот ответ, и ответ JohnnyHK являются релевантными, то есть вам нужно $ in.
tscizzle
88

Я чувствую, $allчто будет более подходящим в этой ситуации. Если вы ищете человека, который любит суши, вы делаете:

PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})

Как вы можете захотеть отфильтровать ваш поиск, например, так:

PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})

$inэто как ИЛИ и $allкак И. Проверьте это: https://docs.mongodb.com/manual/reference/operator/query/all/

Pobe
источник
Извините, это неправильный ответ на мой вопрос. Я не ищу точное соответствие, а только для массивов, которые содержат по крайней мере указанное значение.
Людвиг Магнуссон
18
Это совершенно правильный ответ на ваш вопрос! Для одного значения нет разницы в использовании $ all или $ in. Если у вас есть несколько значений, таких как "суши", "бананы", $ all ищет людей, у которых есть "суши" И "бананы" в их массиве favourFood, если при использовании $ вы получаете людей, у которых есть "суши" ИЛИ "бананы" "в их любимом массиве еды.
Jodo
да, нет $ содержит, но $ все вроде как
datdinhquoc
3
Лучший ответ.
Николай Ценков
65

В случае, если массив содержит объекты, например, if favouriteFoodsявляется массивом следующих объектов:

{
  name: 'Sushi',
  type: 'Japanese'
}

Вы можете использовать следующий запрос:

PersonModel.find({"favouriteFoods.name": "Sushi"});
Кфир Эрез
источник
2
Это легко лучший ответ. Намного легче использовать, когда вы спешите.
Uber Schnoz
Это должен быть выбранный ответ. Если вы имеете дело с запросом массива вложенных документов в MongoDB, это то, как вы это делаете. Не уверен, что это наиболее эффективно, но если это все, что вы пытаетесь сделать, это все, что вам нужно.
Кайл Л.
32

Если вам нужно найти документы, содержащие элементы NULL, внутри массива поддокументов, я нашел этот запрос, который работает довольно хорошо:

db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})

Этот запрос взят из этого поста: массив запросов MongoDb с нулевыми значениями

Это была отличная находка, и она работает намного лучше, чем моя первоначальная и неправильная версия (которая отлично работала только для массивов с одним элементом):

.find({
    'MyArrayOfSubDocuments': { $not: { $size: 0 } },
    'MyArrayOfSubDocuments._id': { $exists: false }
})
Хесус Кэмпон
источник
3

Хотя согласитесь с find () наиболее эффективным в вашем случае использования. Тем не менее, существует $ match структуры агрегации, чтобы упростить запрос большого количества записей и генерировать небольшое количество результатов, которые имеют ценность для вас, особенно для группировки и создания новых файлов.

  PersonModel.aggregate([
            { 
                 "$match": { 
                     $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ]  }
             },
             { $project : {"_id": 0, "name" : 1} }
            ]);
Amitesh
источник
не работает с mongodb 4.2 .. пожалуйста, ответьте
vimmi
Какую ошибку вы получаете, пожалуйста, подробно опишите?
Амитеш
3

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}
Гаутама
источник
1

Для Loopback3 все приведенные примеры не работали для меня или так же быстро, как использование REST API. Но это помогло мне понять, какой именно ответ мне нужен.

{"where":{"arrayAttribute":{ "all" :[String]}}}

Марк Райан Ороса
источник
1
Вы спасатель, спасибо! Где это задокументировано, и я пропустил это? Вы можете опубликовать ссылку, пожалуйста? Спасибо.
user2078023
-3

Если вы хотите использовать что-то вроде оператора «Содержит» через 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);
}
Alingenomen
источник
-26

Я знаю, что эта тема старая, но для будущих людей, которые могут задаться тем же вопросом, можно сделать еще одно невероятно неэффективное решение:

PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});

Это позволяет избежать всех оптимизаций MongoDB, поэтому не используйте их в производственном коде.

user3027146
источник
Из любопытства, есть ли преимущество в том, чтобы делать это таким образом?
Людвиг Магнуссон
5
Это невероятно неэффективно по сравнению с принятым ответом; он обходит все оптимизации, которые Монго ставит за кулисы для прямой находки, как в принятой.
невольный
1
Это именно тот ответ, который мне нужен в моем случае, хотя! Спасибо "пользователь" :)
Василий