Раздел 36 · MongoDB с нуля

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

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

В прошлом уроке мы научились находить нужные документы. Но витрина магазина показывает не «все подходящие книги целиком», а название и цену, от дешёвых к дорогим, по десять штук на странице. Сегодня три инструмента для этого: проекция, сортировка и пагинация. Примеры на учебной базе магазина.

Ценник, а не паспорт книги

На полке магазина у книги ценник: название и цена. Паспорт с числом страниц, годом и отзывами лежит на складе. Показывать покупателю паспорт целиком незачем, а таскать его со склада ради ценника расточительно.

Проекция это второй аргумент find, который говорит, какие поля вернуть. Сервер отрежет лишнее до отправки, и по сети поедет меньше данных.

Включить или исключить

Проекция работает в одном из двух режимов. Можно перечислить поля, которые нужны, поставив им 1, а можно поля, которые не нужны, поставив 0:

db.books.find({ genre: 'учебник' }, { title: 1, price: 1 })
db.books.find({ genre: 'учебник' }, { reviews: 0, tags: 0, author: 0 })
[
  { _id: 7, title: 'Грокаем алгоритмы', price: 1290 },
  { _id: 8, title: 'Совершенный код', price: 2490 }
]
[
  {
    _id: 7,
    title: 'Грокаем алгоритмы',
    genre: 'учебник',
    price: 1290,
    pages: 288,
    year: 2016
  },
  {
    _id: 8,
    title: 'Совершенный код',
    genre: 'учебник',
    price: 2490,
    pages: 896,
    year: 1993
  }
]

Обрати внимание: в первом запросе _id пришёл, хотя мы его не просили. Это исключение из правила: _id включается всегда, пока явно не написать _id: 0. Отсюда частая запись { title: 1, _id: 0 } из прошлого урока.

Смешивать режимы нельзя. Нельзя одновременно сказать «дай только название» и «убери отзывы»:

db.books.find({}, { title: 1, reviews: 0 })
Uncaught MongoServerError[Location31254]: Cannot do exclusion on field reviews in inclusion projection

Единственное поле, которое можно исключить в режиме включения, это _id.

Для вложенных документов работает уже знакомая точечная нотация, а для массивов есть оператор $slice, который берёт первые элементы:

db.books.find({ genre: 'роман' }, { title: 1, 'author.name': 1, _id: 0 })
db.books.find({ _id: 7 }, { title: 1, reviews: { $slice: 1 }, _id: 0 })
[
  { title: 'Мастер и Маргарита', author: { name: 'Булгаков' } },
  { title: 'Мёртвые души', author: { name: 'Гоголь' } }
]
[
  {
    title: 'Грокаем алгоритмы',
    reviews: [ { user: 'Павел', rating: 5, text: 'Понятно даже новичку' } ]
  }
]

$slice полезен, когда в документе длинный массив, а на странице нужны только последние отзывы.

урок из курса

Урок из курса «MongoDB с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.