В MongoDB у каждого документа уже есть уникальный _id. Обычно это ObjectId, и для внутренней идентификации его достаточно. Но если вам нужен человекочитаемый номер заказа, заявки или счета, создайте отдельное поле, например number, поставьте на него уникальный индекс и генерируйте значение аккуратно
Пример документа:
{
_id: ObjectId("665f1c1a7b8c2e0012a4d111"),
number: 1001,
status: "new",
total: 2500
}
_id нужен базе, number удобен людям
- Вариант 1. Использовать ObjectId
- Вариант 2. Уникальный индекс на поле
- Вариант 3. Счетчик для последовательных номеров
- Что выбрать
- Частые ошибки
- Делают номер через countDocuments
- Не добавляют уникальный индекс
- Используют номер как единственную защиту
- Как сделать красивый номер
- Проверка перед запуском
- Что почитать дальше по MongoDB
- Как проверить результат на практике
Вариант 1. Использовать ObjectId
Если номер нужен только для поиска документа в приложении, ничего добавлять не нужно. Используйте _id:
db.orders.findOne({ _id: ObjectId("665f1c1a7b8c2e0012a4d111") })
Минус: такой номер длинный и неудобный для клиента
Вариант 2. Уникальный индекс на поле
Если номер задается приложением:
db.orders.createIndex({ number: 1 }, { unique: true })
Теперь MongoDB не позволит вставить два заказа с одинаковым number
Проверка:
db.orders.insertOne({ number: 1001, status: "new" })
db.orders.insertOne({ number: 1001, status: "new" })
Вторая вставка должна завершиться ошибкой повторяющегося значения
Вариант 3. Счетчик для последовательных номеров
Создайте коллекцию counters:
db.counters.insertOne({
_id: "orderNumber",
seq: 1000
})
Получение следующего номера:
const counter = db.counters.findOneAndUpdate(
{ _id: "orderNumber" },
{ $inc: { seq: 1 } },
{ returnDocument: "after" }
)
db.orders.insertOne({
number: counter.seq,
status: "new"
})
Смысл в том, что $inc увеличивает счетчик, а findOneAndUpdate возвращает новое значение
Что выбрать
Для внутренней логики используйте _id
Для публичного номера заказа используйте отдельное поле number
Для уникальности обязательно добавьте уникальный индекс
Для последовательных номеров используйте счетчик, но помните: последовательная нумерация требует аккуратности при высокой нагрузке
Частые ошибки
Делают номер через countDocuments
Если два запроса одновременно увидят одинаковое количество документов, они могут создать одинаковый номер. Так лучше не делать
Не добавляют уникальный индекс
Проверка в коде не заменяет ограничение в базе. Уникальный индекс — последняя защита от повторяющихся номеров
Используют номер как единственную защиту
Если пользователь открывает заказ по номеру, все равно проверяйте владельца заказа
Как сделать красивый номер
Часто в интерфейсе нужен не просто 1001, а номер вроде ORD-1001. В базе можно хранить число отдельно, а строку собирать в приложении:
{
number: 1001,
status: "new"
}
В интерфейсе:
ORD-1001
Так сортировка и поиск по числу остаются удобными. Если хранить весь номер строкой, не забывайте про одинаковую длину или отдельное числовое поле для сортировки
Проверка перед запуском
Перед включением последовательных номеров на рабочем проекте проверьте два параллельных создания документа. Если оба получают разные номера, счетчик работает правильно
Что почитать дальше по MongoDB
Если нужен общий маршрут по теме, откройте рубрику MongoDB. Для соседних задач пригодятся эти разборы:
- Как сделать, чтобы ActiveRecord MongoDB отдавал модели, а не массив
- Ошибка создания документа в MongoDB после генерации _id: как исправить
- Discord bot на Python и MongoDB: как задать проверку
- Failed to start MongoDB database server: что проверить
Как проверить результат на практике
Для MongoDB-материала полезно делать три проверки: сначала выполнить команду на маленьком наборе тестовых документов, затем посмотреть результат через find() или MongoDB Compass, а после этого повторить действие на копии реальной структуры данных. Если запрос меняет документы, сначала запускайте его с фильтром на один документ и только потом расширяйте условие
Еще одна хорошая привычка — записывать исходное состояние и ожидаемый результат. Например: было три документа, после обновления изменился только один; был массив из пяти элементов, после операции остался нужный элемент; индекс появился в getIndexes(). Такая проверка быстро показывает, что вы исправили именно задачу, а не просто получили команду без ошибки



