Изучаем MongoDB: запросы к Документам — 2
Перевод: Paras — Learn MongoDB: Query Documents — II
Продолжим прошлую статью о работе с документами. В предыдущем посте я рассмотрел простые запросы и запросы к встроенным документам с помощью нескольких типов операторов, доступных в mongodb. В этом посте рассмотрим операторы, связанные с массивами и оценкой данных.
Содержание:
- Операторы оценочного запроса
- Запросы к массивам
- Операторы массивов
Новая структура документа в коллекции покемонов:
Вместо одной weakness теперь мы будем сохранять для покемонов набор weaknesses.
{ name: "pikachu", type: "Electric", stats: { health: 40, attack: 50, defense: 45 }, level: 16, weakness: ["ground", "grass", "dragon"], // now it's array evolution: "raichu", moves: [ {name: "quick attack", dmg: 40}, {name: "thunder bolt", dmg: 90}, {name: "irontail", dmg: 50} ] }
Операторы оценочного запроса
Об операторах оценочного запроса сказать особо нечего. Они просто проводят оценку.
- $regex (позволяет указать выражение регулярного выражения в вашем запросе)
- $text (полезно при работе с индексами в mongodb, поэтому сейчас не рассматривается)
- $jsonSchema (Для проверки данных по схеме)
- $expr
- $mod ( { field: { $mod: [ divisor, remainder ] } })
Использование:
# $regex находит покемонов с "pi" в их именах > db.persons.find({ description: {$regex: /pi/}}) # создать валидатор при создании коллекции # давайте создадим простую схему для коллекции покемонов > db.createCollection( <collection>, { validator: { required: ["name", "type", "level"], $jsonSchema: { name: { bsonType: "string" }, type: { bsonType: "string" }, level: { bsonType: "int", minimum: 1 } } }}) # $mod помогают выполнять операцию модификации поля с числом # получаем всех покемонов, чей уровень делится на 5 > db.pokemons.find({level: {$mod: [5, 0]}}) # divisor, remainder
$expr : Этот оператор помогает использовать выражения агрегирования в запросе.
- Сравнение полей: вы можете использовать оператор $expr для сравнения двух полей. Например. мы можем сравнить уровень и поле защиты в нашей коллекции покемонов
# получить всех покемонов, у которых level превышает их defense > db.pokemons.find({$expr: {$gt: ["$level", "$stats.defense"] }})
Есть еще один оператор $cond, который помогает нам создавать условия (if, then, else). Мы рассмотрим его позже в этой серии.
Запросы к массивам
Теперь мы узнаем, как работать с массивами внутри документов.
Простой запрос элемента в массиве : Вы можете запрашивать массивы в обычном режиме, а обо всем остальном позаботится mongo. Он проверит, существует ли значение в массиве или нет.
# найти всех покемонов с weakness "ground" > db.pokemons.find({weakness: "ground"}) # если вы сделаете что-то подобное, он проверит точное совпадение массива > db.pokemons.find({weakness: ["ground"]})
Запрос по индексу в массиве : Можно запросить элемент по конкретному индексу в массиве. Просто укажите индекс, аналогично указанию ключа в объекте.
> db.pokemons.find({"weakness.2": "electric" })
Что, если у нас есть встроенные документы внутри массива ?? (Подсказка: аналогично встроенным документам)
# найти всех покемонов, у которых есть "quick attack" > db.pokemons.find({"moves.name": "quick attack"})
Все может стать сложным, когда структура документа станет более сложной. В нашем примере выше мы искали только один ключ. Давайте посмотрим еще несколько примеров, которые могут доставить нам неприятности. Мы будем использовать другую структуру документа вместо структуры наших покемонов.
// e.g collection = articles { comments: [ { name: "Nina", rating: 3 }, { name: "Colt", rating: 4 }, { name: "Rico", rating: 2.5} ] }
# допустим нам нужно найти статьи, в которых рейтинг больше 2 # это не сработает, так как будет поиск на точное совпадение > db.articles.find({"comments": { name: "Nina", rating: {$gt: 2}}}) # это тоже не сработает # имя может совпасть с другим комментарием, чем рейтинг > db.articles.find({"comments.name": "Nina", "comments.rating": {$gt: 2}})
Эта проблема решается с помощью операторов.
Операторы массивов
Операторов у массивов немного, но они очень полезны.
- Operators:
$all
,$size
,$elemMatch
$all : Рассмотрим его на примере. Допустим нам надо найдите всех покемонов, чьи weakness имеет значение ground и electric.
> db.pokemons.find({weakness: {$all: ["ground", "electric"]}}) # Не пытайтесь выполнить следующий запрос, потому что # будет искать точное совпадение > db.pokemons.find({weakness: ["ground", "electric"]})
$all
проверяет, находится ли все элементы в массиве. Это могут быть объекты, отдельные значения или массивы. Он также игнорирует порядок, в котором вы указали элементы в запросе.
$size : Запрос на размер массива
# найти всех покемонов с 4 ходами > db.pokemons.find({moves: {$size: 4}})
Оператор $size принимает только точное число. Запросы больше или меньше невозможны.
$elemMatch : Очень нужный оператор!!! … Шучу. Вы помните пример, который мы использовали при запросе встроенных документов в массиве? $elemMatch может нам в этом помочь !! Посмотрим как
// e.g collection = articles { comments: [ { name: "Nina", rating: 3 }, { name: "Colt", rating: 4 }, { name: "Rico", rating: 2.5} ] }
# найти все статьи, в рейтинг Nina имеет значение больше 2 > db.articles.find({ comments: { $elemMatch: {name: "Nina", rating: {$gt: 2}} } })
Что только что произошло в этом запросе ??
$elemMatch помог нам определить, как должен выглядеть документ, который может соответствовать нашему запросу. Круто, не правда ли?
Заключение
Итак, мы все рассмотрели для запросов к массивам и встроенным документам. Если у вас есть вопросы, не стесняйтесь задавать их в комментариях (в блоге автора статьи). Я постараюсь ответить на них.