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

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

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, поэтому не используйте их в производственном коде.

Другие вопросы по тегам