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

Поле _id и тип ObjectId

junior~15 мин

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

В прошлом уроке каждая вставка возвращала _id. Когда мы задавали его сами, это было наше число, когда нет, MongoDB придумывала что-то вроде ObjectId('6ac9371c7c7353d2ce52946e'). Сегодня разбираемся, зачем документу это поле, из чего состоит такой номер и когда его стоит выбирать самому.

Номерок в гардеробе

В гардеробе театра пальто никто не ищет по цвету или фасону. Тебе дают номерок, и по нему пальто находят за секунду. Двух одинаковых номерков в одном гардеробе не бывает.

У каждого документа в MongoDB есть такой номерок, поле _id. Три правила:

  • поле есть всегда: если ты его не указал, MongoDB добавит его сама;
  • значение уникально внутри коллекции: второй документ с тем же _id не вставится;
  • по _id всегда есть индекс, поэтому поиск по нему быстрый.

В терминах реляционной теории _id это первичный ключ, только с фиксированным именем.

Что внутри ObjectId

Если _id не указан, MongoDB подставляет значение типа ObjectId. Это 12 байт, которые печатаются как 24 шестнадцатеричные цифры. Возьмём номер и разрежем его:

66f8a1c2   e4b0a1b2c3   d4e5f6
└──┬───┘   └───┬────┘   └─┬──┘
 время      случайное    счётчик
 4 байта    5 байт       3 байта
  • Время. Первые 4 байта это момент создания в секундах от 1970 года.
  • Случайное значение. 5 байт генерируются один раз на процесс, чтобы два сервера не выдали одинаковых номеров.
  • Счётчик. 3 байта растут на единицу с каждым новым номером внутри процесса.

Из-за этой раскладки номер можно получить без обращения к базе и без согласования с другими серверами: каждый, кто создаёт документ, придумывает номер сам, и номера не совпадут. Обычно ObjectId генерирует даже не сервер, а драйвер в приложении.

Время создания можно достать прямо из номера:

const id = ObjectId('66f8a1c2e4b0a1b2c3d4e5f6')
id.getTimestamp()
id.toString()
ISODate('2024-09-29T00:39:30.000Z')
66f8a1c2e4b0a1b2c3d4e5f6

Отсюда полезное следствие: документы, вставленные позже, обычно получают номера больше. Сортировка по _id примерно совпадает с сортировкой по времени создания. Примерно, потому что внутри одной секунды порядок решает счётчик, а у разных процессов счётчики свои.

Время в ObjectId записано в секундах. Если нужно точное время события, храни его отдельным полем, например created_at, как в заказах учебной базы.

урок из курса

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

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