Введение
В этой лабораторной работе вы научитесь эффективно запрашивать массивы в MongoDB. Массивы являются фундаментальной структурой данных в MongoDB, позволяющей хранить списки значений в одном поле документа. Эта лабораторная работа проведет вас через основные методы работы с данными массивов, от простого сопоставления элементов до более сложных запросов с использованием специализированных операторов. Вы научитесь находить документы на основе содержимого массива, его размера и позиции элементов, что является критически важным навыком для любого разработчика, работающего с MongoDB.
Начало работы и базовое сопоставление массивов
На этом первом этапе вы подключитесь к серверу MongoDB, создадите базу данных и вставите некоторые примеры данных. Затем вы выполните базовые запросы для поиска документов путем сопоставления элементов внутри массива.
Сначала откройте MongoDB Shell (mongosh), чтобы взаимодействовать с вашей базой данных. Эта команда подключит вас к запущенному экземпляру MongoDB.
mongosh
Оказавшись в оболочке, вы увидите приглашение test>. Давайте переключимся на новую базу данных с именем arraylab. Команда use создает базу данных, если она еще не существует, и переключает текущий контекст на нее.
use arraylab
Теперь давайте вставим несколько документов в новую коллекцию под названием products. Каждый документ представляет продукт и содержит два поля-массива: tags и colors. Скопируйте и вставьте следующую команду в свою оболочку:
db.products.insertMany([
{
name: "Laptop",
tags: ["electronics", "computer", "work"],
colors: ["silver", "black", "blue"]
},
{
name: "Smartphone",
tags: ["electronics", "mobile", "communication"],
colors: ["red", "blue", "green"]
},
{
name: "Headphones",
tags: ["electronics", "audio", "music"],
colors: ["black", "white"]
}
]);
Чтобы найти документ, содержащий определенный элемент в массиве, вы можете запросить это значение напрямую. Этот запрос находит все продукты, у которых тег "mobile".
db.products.find({ tags: "mobile" });
Пример вывода:
[
{
_id: ObjectId("..."),
name: 'Smartphone',
tags: [ 'electronics', 'mobile', 'communication' ],
colors: [ 'red', 'blue', 'green' ]
}
]
Вы также можете найти документы, где массив точно соответствует последовательности элементов. Этот запрос находит продукт, массив tags которого точно равен ["electronics", "computer", "work"] в этом конкретном порядке.
db.products.find({ tags: ["electronics", "computer", "work"] });
Пример вывода:
[
{
_id: ObjectId("..."),
name: 'Laptop',
tags: [ 'electronics', 'computer', 'work' ],
colors: [ 'silver', 'black', 'blue' ]
}
]
Вы можете оставаться в оболочке mongosh для следующих шагов. Чтобы выйти из оболочки в любое время, введите exit и нажмите Enter.
Использование операторов запросов к массивам $all и $in
Простое сопоставление полезно, но MongoDB предоставляет мощные операторы для более сложных запросов к массивам. На этом этапе вы научитесь использовать $all для сопоставления нескольких элементов независимо от порядка и $in для сопоставления любого из списка элементов.
Мы продолжим использовать коллекцию products из предыдущего шага.
Оператор $all выбирает документы, в которых поле массива содержит все указанные элементы. Порядок элементов в запросе не имеет значения. Давайте найдем продукты, которые доступны как в "синем", так и в "серебристом" цвете.
db.products.find({ colors: { $all: ["blue", "silver"] } });
Пример вывода:
[
{
_id: ObjectId("..."),
name: 'Laptop',
tags: [ 'electronics', 'computer', 'work' ],
colors: [ 'silver', 'black', 'blue' ]
}
]
Оператор $in выбирает документы, в которых поле массива содержит хотя бы одно из указанных значений. Он действует как условие "ИЛИ" для элементов массива. Давайте найдем продукты, у которых есть тег "music" или тег "work".
db.products.find({ tags: { $in: ["music", "work"] } });
Пример вывода:
[
{
_id: ObjectId("..."),
name: 'Laptop',
tags: [ 'electronics', 'computer', 'work' ],
colors: [ 'silver', 'black', 'blue' ]
},
{
_id: ObjectId("..."),
name: 'Headphones',
tags: [ 'electronics', 'audio', 'music' ],
colors: [ 'black', 'white' ]
}
]
Эти операторы обеспечивают гораздо большую гибкость при поиске документов на основе содержимого их массивов.
Запросы по размеру и позиции массива
Иногда важны количество элементов в массиве или позиция определенного элемента. Этот шаг охватывает, как выполнять запросы на основе размера массива с использованием оператора $size и по позиции элемента с использованием точечной нотации.
Оператор $size выбирает документы, в которых массив содержит определенное количество элементов. Давайте найдем продукты, для которых доступны ровно два цвета.
db.products.find({ colors: { $size: 2 } });
Пример вывода:
[
{
_id: ObjectId("..."),
name: 'Headphones',
tags: [ 'electronics', 'audio', 'music' ],
colors: [ 'black', 'white' ]
}
]
Чтобы выполнить запрос по элементу в определенной позиции (индексе) массива, вы используете точечную нотацию в формате "fieldName.index". Помните, что индексы массивов начинаются с нуля, поэтому первый элемент находится по индексу 0. Этот запрос находит все продукты, где первый элемент в массиве tags — "electronics".
db.products.find({ "tags.0": "electronics" });
Пример вывода:
[
{
_id: ObjectId("..."),
name: 'Laptop',
tags: [ 'electronics', 'computer', 'work' ],
colors: [ 'silver', 'black', 'blue' ]
},
{
_id: ObjectId("..."),
name: 'Smartphone',
tags: [ 'electronics', 'mobile', 'communication' ],
colors: [ 'red', 'blue', 'green' ]
},
{
_id: ObjectId("..."),
name: 'Headphones',
tags: [ 'electronics', 'audio', 'music' ],
colors: [ 'black', 'white' ]
}
]
Теперь давайте найдем продукт, где второй цвет (индекс 1) — "black".
db.products.find({ "colors.1": "black" });
Пример вывода:
[
{
_id: ObjectId("..."),
name: 'Laptop',
tags: [ 'electronics', 'computer', 'work' ],
colors: [ 'silver', 'black', 'blue' ]
}
]
Запросы к массивам вложенных документов
Распространенным и мощным шаблоном в MongoDB является использование массивов, содержащих другие документы. Они известны как вложенные документы. Запросы к ним требуют специального оператора, $elemMatch, чтобы гарантировать, что все условия применяются к одному и тому же элементу массива.
Сначала добавим несколько новых продуктов с массивом specs. Каждый элемент в массиве specs является документом с полями ram, storage и price.
db.products.insertMany([
{
name: "Gaming Laptop",
tags: ["electronics", "computer", "gaming"],
specs: [
{ ram: 16, storage: 512, price: 1200 },
{ ram: 32, storage: 1024, price: 2000 }
]
},
{
name: "Office Laptop",
tags: ["electronics", "computer", "work"],
specs: [
{ ram: 8, storage: 256, price: 600 },
{ ram: 16, storage: 512, price: 800 }
]
}
]);
Теперь предположим, мы хотим найти ноутбук, который имеет конфигурацию с оперативной памятью не менее 16 ГБ и ценой менее 1000 долларов. Простой запрос может показаться правильным, но может привести к неверным результатам. Например, db.products.find({ "specs.ram": { $gte: 16 }, "specs.price": { $lt: 1000 } }) сопоставит документ, если один вложенный документ имеет достаточный объем ОЗУ, а другой — достаточно низкую цену.
Чтобы гарантировать, что один вложенный документ в массиве удовлетворяет всем условиям, вы должны использовать оператор $elemMatch.
db.products.find({
specs: {
$elemMatch: { ram: { $gte: 16 }, price: { $lt: 1000 } }
}
});
Этот запрос корректно находит только "Office Laptop", поскольку у него есть элемент спецификации { ram: 16, storage: 512, price: 800 }, который одновременно удовлетворяет обоим условиям.
Пример вывода:
[
{
_id: ObjectId("..."),
name: 'Office Laptop',
tags: [ 'electronics', 'computer', 'work' ],
specs: [
{ ram: 8, storage: 256, price: 600 },
{ ram: 16, storage: 512, price: 800 }
]
}
]
Проектирование полей массива
Часто при выполнении запроса вам не нужен весь документ. Проецирование позволяет указать, какие поля включать или исключать. Для массивов MongoDB предоставляет специальные операторы проецирования, такие как $slice, для управления тем, какие элементы массива возвращаются.
Оператор проецирования $slice возвращает подмножество массива. Вы можете указать положительное число, чтобы получить первые N элементов, или отрицательное число, чтобы получить последние N элементов.
Извлечем все продукты, но для каждого из них вернем только name и первые два tags. Первый аргумент find, {}, является пустым документом запроса, который соответствует всем документам. Второй аргумент — это документ проецирования. Мы используем _id: 0, чтобы исключить поле _id по умолчанию.
db.products.find({}, { name: 1, tags: { $slice: 2 }, _id: 0 });
Пример вывода:
[
{ "name": "Laptop", "tags": ["electronics", "computer"] },
{ "name": "Smartphone", "tags": ["electronics", "mobile"] },
{ "name": "Headphones", "tags": ["electronics", "audio"] },
{ "name": "Gaming Laptop", "tags": ["electronics", "computer"] },
{ "name": "Office Laptop", "tags": ["electronics", "computer"] }
]
Другим полезным оператором проецирования является позиционный оператор $. Когда вы запрашиваете массив, этот оператор гарантирует, что в проецируемом поле массива будет возвращен только первый элемент, соответствующий условию запроса.
Найдем продукты с тегом "mobile" и вернем только name и этот конкретный соответствующий тег из массива tags.
db.products.find({ tags: "mobile" }, { name: 1, "tags.$": 1, _id: 0 });
Пример вывода:
[{ "name": "Smartphone", "tags": ["mobile"] }]
Это очень эффективно для извлечения только релевантных частей большого массива без необходимости обработки всего массива в вашем приложении.
Резюме
В этой лабораторной работе вы изучили основные методы запросов к массивам в MongoDB. Вы начали с базового сопоставления элементов и перешли к использованию мощных операторов массивов, таких как $all, $in и $size. Вы также узнали, как выполнять запросы по позиции элемента и как правильно запрашивать массивы вложенных документов с помощью $elemMatch. Наконец, вы научились формировать вывод ваших запросов, проецируя конкретные элементы массива с помощью $slice и позиционного оператора $. Эти навыки необходимы для эффективного управления и извлечения данных из сложных структур документов в MongoDB.

