MongoDB: Запросы

Хоть и некоторые писали относительно моего предыдущего топика MongoDB: Создание, обновление и удаление документов, что это пересказ офф. документации, я с этим не полностью согласен. Мне показалось, что информация в нём оказалось кому-то полезной, поэтому выкладываю продолжение.

Find — аналог SELECT в MySQL. Используется для выборки документов из MongoDB. Возвращает массив документов в виде коллекции, если документов нет — пустую коллекцию. Пример:

> db.users.find();

Вернёт всех пользователей из коллекции.

> db.users.find( { age: 27 } );

Вернёт всех пользователей, у которых возраст равен 27.

Можно указывать несколько параметров, например, вот так:

> db.users.find( { username: “joe”, age: 27 } );

Иногда бывает необходимо получить какие то конкретные поля из документов. В этом случае запрос выглядит так:

> db.users.find( { }, { username: 1, email: 1 } );

В итоге получим всех пользователей только с полями «username» и «email» + поле "_id", которое возращается всегда по умолчанию. Если поле "_id" или какое-либо другое не нужно, мы можем это указать вот так:

> db.users.find( { }, { username: 1, email: 1, _id: 0 } );

Запросы с условием

Операторы: $lt — меньше, $lte — меньше или равно, $gt — больше, $gte — больше или равно, $ne — не равно.
Примеры использования:
Получаем всех пользователей, возраст которых больше 18 и меньше 30

> db.users.find( { age: { $gte: 18, $lte: 30 } } );

Получаем всех пользователей, «username» которых не равен “joe”

> db.users.find( { username: { $ne: “joe” } } );

Допустим, у нас коллекция хранит лотерейные билеты с номерами и нам нужно найти только те, в которых есть победившие номера 390, 754, 454. В таком случае мы используем оператор $in:

> db.users.find( { numbers: { $in: [ 390, 754, 454 ] } } );

т.е. билет должен содержать один из этих номеров.
Противоположным оператору $in является $nin. Он, по аналогии, получает только те билеты, где нет, указанных выше, номеров.
Оператор $or используется при запросах, когда нужно выбрать документы по совпадению одному из значений. Например, нам нужно выбрать все билеты с номером 547 или где поле «winner» равно true:

> db.users.find( { $or: [ { tickect_number: 547 }, { winner: true } ] } );

Оператор $mod используется для выборки ключей, значения которых делятся на первый аргумент и остаток от деления получается равным второму аргументу. Звучит непонятно, вот более наглядно:

> db.users.find( { user_id: { $mod: [ 5, 1 ] } } );

Такой запрос вернёт всех пользователей, «user_id» которых равен 1, 6, 11, 16 и так далее.
Чтоб получить всех пользователей, кроме вышеуказанных можно использовать оператор $not:

> db.users.find( { user_id: { $not: { $mod: [ 5, 1 ] } } } );

Получим пользователей с «user_id» 2, 3, 4, 5, 7, 8, 9, 10, 12 и так далее.
Существует также оператор для проверки существует ли какой то ключ или нет — $exist

> db.c.find({"z" : {"$in" : [null], "$exists" : true}});

Так можно выбрать все документы коллекции, где существует ключ “z” и он равен null.

Регулярные выражения

Все мы знаем, что регулярные выражения очень мощная штука. MongoDB использует Perl-совместимые регулярные выражения.
Вот так можно найти всех пользователей с именем joe или Joe:

> db.users.find( { "name" : /^joe$/i } );

Вообщем есть где разгуляться. Есть куча сервисов для того же javascript’a, где можно писать и проверять свои регулярки.

Запросы в массивах

Допустим есть у нас коллекция food и мы туда вставляем документ с массивом фруктов

> db.food.insert( { "fruit" : [ "apple", "banana", "peach" ] } );

То вот такой запрос

> db.food.find({"fruit" : "banana"});

успешно его найдёт.
Если нужно выбрать документы больше, чем по одному элементу массива, то мы можем использовать оператор $all

> db.food.find( { fruits: { $all : [ "apple", "banana" ] } } );

Такой запрос вернёт все документы, в массиве фруктов которых, есть и яблоки, и бананы.
Получить документы по полному совпадению элементов в массиве можем так:

> db.food.find( { fruits: [ "apple", "banana", "peach" ] } );

Есть у нас блог, в нём хранятся комментарии. Мы хотим получить первые 10 комментариев. На помощь нам приходит оператор $slice:

> db.blog.posts.findOne( { }, { "comments" : { "$slice" : 10 } } );

findOne — работает аналогично find, но возвращает первый совпавший документ.

Если нужно получить последние 10 комментариев пишем вот так:

> db.blog.posts.findOne( { }, { "comments" : { "$slice" : -10 } } );

Также $slice умеет получать документы из середины:

> db.blog.posts.findOne( { }, { comments : { "$slice" : [ 23, 10 ] } });

в этом случае будет пропущено 23 начальных элемента и вернутся элементы с 24 по 34, если это возможно

Команды limit, skip и sort

Для того, чтоб получить ограниченное количество документов по запросу используется команда limit:

> db.users.find().limit(3);

Вернёт первых 3-х пользователей.
Для того, чтоб пропустить несколько документов и получить все остальные используется команда skip:

> db.users.find().skip(3);

Получаем всех пользователей, кроме первых трёх.
Для сортировки используется команда sort. Сортировка может быть по возрастанию (1) и по убыванию (- 1). Сортировка может производится по нескольким ключам в порядке их приоритета:

> db.users.find().sort( { username : 1, age : -1 } );

Все эти три команды можно использовать вместе:

> db.stock.find( { "desc" : "mp3" } ).limit(50).skip(50).sort( { "price" : -1 } );

Использование команды skip с большими значениями работает не особо быстро. Рассмотрим это на примере пагинации:

Самый простой способ сделать пагинацию это в первый раз вернуть фиксированное количество документов, а потом каждый раз смещать диапазон на это значение

> var page1 = db.foo.find(criteria).limit(100)
> var page2 = db.foo.find(criteria).skip(100).limit(100)
> var page3 = db.foo.find(criteria).skip(200).limit(100)

но это будет работать довольно медленно. Допустим мы будет делать выборку относительно даты создания документа:

> var page1 = db.foo.find().sort( {"date" : -1} ).limit(100);

Сортируем по убыванию и берём первые 100. Чтоб не делать skip мы можем получить дату последнего документа и сделать запрос уже относительно этой даты:

var page2 = db.foo.find( { "date" : { "$gt" : latest.date } } );

Так можно избежать использования команды skip для больших значений.

По сути это основные вещи относительно запросов в MongoDB. Надеюсь это кому-нибудь пригодится.