Знайдіть документ із масивом, який містить конкретне значення


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, який приходить з RDBMS, як MySQL, виявити, що такі рішення працюють так просто, не потребуючи ПРИЄДНАННЯ та додаткових таблиць, змушує мене замислитися, чому я не запускався на Mongo раніше. Але це не означає, що будь-яка СУБД є вищою за інші - це залежить від вашого випадку використання.
Ірвін Лім

9
Не помиляйтеся. Навіть якщо це список дикту, ви все одно можете запитати його таким чином. Зразок: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Aminah Nuraini

3
Що відбувається, коли я хочу знайти масив, що містить принаймні два рядки?
Аеро Ван

151

У $containsmongodb немає оператора.

Ви можете використовувати відповідь від JohnnyHK, як це працює. Найближча аналогія до того, що містить монго, - це $in, використовуючи цей запит, виглядатиме так:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);

10
Це правильно? Хіба mongodb не очікує масиву значень при використанні $ in? як {name: {$ in: ["Пол", "Дейв", "Ларрі", "Адам"]}}?
Людвіг Магнуссон

37
Так? Це зайве. $inвикористовується, коли у вас є декілька значень запитів, і документ повинен відповідати одному з них. Для зворотного (про що йдеться в цьому питанні) відповідь JohnnyHK правильна. Я збирався подати заявку, але, мабуть, ця відповідь може бути корисною для інших людей, які опинилися на цій сторінці.
МалькольмОкеан

4
Але це допомогло мені насправді запит із кількома значеннями: D Дякую!
Олександр Бур'є

10
Дякую. Це те, що я насправді шукав, спосіб пошуку кількох значень:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli

@MalcolmOcean правильний, оскільки оператор $ in є для зворотного, маючи масив як значення . Поле є масивом то , що питання питає о. Однак якщо і поле, і значення - це масиви, то і ця відповідь, і JohnnyHK є релевантними, тобто вам потрібно $ in.
tscizzle

87

Я вважаю, що $allбуло б доречніше в цій ситуації. Якщо ви шукаєте людину, яка займається суші, ви робите:

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

Оскільки ви, можливо, хочете більше фільтрувати пошук, наприклад:

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

$inце як АБО і $allяк І. Перевірте це: https://docs.mongodb.com/manual/reference/operator/query/all/


Вибачте, це неправильна відповідь на моє запитання. Я шукаю не точну відповідність, а лише масиви, що містять принаймні вказане значення.
Людвіг Магнуссон

17
Це ідеально правильна відповідь на ваше запитання! Для одного значення немає різниці у використанні $ all або $ in. Якщо у вас є кілька значень на кшталт "суші", "банани", $ все шукає людей, які мають "суші" та "банани" у своєму улюбленому масиві їжі, якщо за допомогою $ у вас ви отримуєте людей, які мають "суші" АБО "банани "у їх улюбленому масиві їжі.
Джодо

так, немає $ містить, але $ все це свого роду
datdinhquoc,

2
Найкраща відповідь.
Микола Ценков

65

У випадку, якщо масив містить об'єкти, наприклад, якщо 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

Хоча згода () найефективніша у вашому користуванні. Однак є $ match Framework агрегації, щоб полегшити запит великої кількості записів та створити малу кількість результатів, які мають значення для вас, особливо для групування та створення нових файлів.

  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
Ви рятувальник життя, дякую! Де це документально зафіксовано, і я це пропустив? Чи можете ви опублікувати посилання, будь ласка? Дякую.
користувач2078023

-3

Якщо ви хочете використовувати щось на зразок оператора "містить" через javascript, ви завжди можете використовувати для цього регулярний вираз ...

напр. Скажіть, ви хочете отримати клієнта, який має ім'я "Bartolomew"

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.