Проекции, сортировка и пагинация

Как вернуть только нужные поля, упорядочить результат и разбить его на страницы.

Проекция — это указание, какие поля документа включить в ответ или исключить из него.

Проекция: только нужные поля

По умолчанию find возвращает документы целиком. Часто это лишнее: приложению нужны два-три поля, а не весь объект. Вторым аргументом find передают проекцию — 1 включает поле, 0 исключает:

db.users.find(
  { city: "Казань" },
  { name: 1, age: 1 }
)

Вернутся только name, age — и _id, который попадает в проекцию автоматически. Чтобы его убрать, исключите явно:

db.users.find(
  { city: "Казань" },
  { name: 1, age: 1, _id: 0 }
)

Правило: в одной проекции нельзя смешивать включение (1) и исключение (0) полей — кроме _id, который можно отключать в любом случае. Либо перечисляете, что оставить, либо что убрать.

Сортировка: sort

Метод sort выстраивает результат: 1 — по возрастанию, -1 — по убыванию. Сначала самые молодые:

db.users.find().sort({ age: 1 })

Сортировать можно по нескольким полям — порядок ключей задаёт приоритет. Сначала по городу (А→Я), внутри города — по убыванию возраста:

db.users.find().sort({ city: 1, age: -1 })

limit и skip: пагинация

limit ограничивает число документов в ответе, skip пропускает первые N. Вместе они дают постраничную выдачу. Топ-3 самых старших пользователя:

db.users.find().sort({ age: -1 }).limit(3)

Вторая страница по 10 записей (пропускаем первые 10, берём следующие 10):

db.users.find()
  .sort({ age: -1 })
  .skip(10)
  .limit(10)

Методы — это звенья одной цепочки (она называется курсором): база применяет сначала фильтр, затем сортировку, затем skip и limit. Поэтому sort почти всегда ставят перед пагинацией — иначе «страницы» будут произвольными.

Осторожно с большими skip

На больших коллекциях skip на десятки тысяч записей работает медленно: база всё равно перебирает пропускаемые документы. Для глубокой пагинации в продакшене используют приём «по последнему _id» (range-пагинация) вместо больших skip. Для учебных объёмов skip/limit вполне годятся.

Итог

  • Проекция (1/0) отдаёт только нужные поля; нельзя смешивать включение и исключение, кроме _id.
  • sort({ поле: 1 }) — по возрастанию, -1 — по убыванию; можно по нескольким полям.
  • limit + skip дают пагинацию; sort ставят до них, а больших skip избегают на проде.
Проверьте себя
1. Как вернуть только поля name и age, без _id?
A{ name: 1, age: 1 }
B{ name: 1, age: 1, _id: 0 }
C{ name: 0, age: 0 }
D{ _id: 1 }
2. Что означает sort({ age: -1 })?
AСортировку по возрастанию возраста
BСортировку по убыванию возраста
CУдаление поля age
DВозврат только одного документа
3. Какая пара методов реализует постраничную выборку?
Afind и count
Bskip и limit
Csort и project
DinsertMany и find
4. Почему sort обычно ставят перед skip и limit?
AИначе запрос не выполнится
BЧтобы страницы были стабильными и упорядоченными, а не произвольными
CЧтобы ускорить вставку
Dsort можно ставить только в конце
Поддержать проект