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


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/


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/


Извините, это неправильный ответ на мой вопрос. Я не ищу точное соответствие, а только для массивов, которые содержат по крайней мере указанное значение.
— Людвиг Магнуссон

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

не работает с 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);
}

-26

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

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

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


Из любопытства, есть ли преимущество в том, чтобы делать это таким образом?
— Людвиг Магнуссон

5
Это невероятно неэффективно по сравнению с принятым ответом; он обходит все оптимизации, которые Монго ставит за кулисы для прямой находки, как в принятой.
— невольный

1
Это именно тот ответ, который мне нужен в моем случае, хотя! Спасибо "пользователь" :)
— Василий
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.