Запросы к массивам MongoDB

MongoDBBeginner
Практиковаться сейчас

Введение

В этой лабораторной работе вы научитесь эффективно запрашивать массивы в 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.