Гайды

Введение в MongoDB: CRUD, BSON и схемы данных

Коллекции и документы, insert/find/update, операторы фильтра, вложение vs ссылки, валидация JSON Schema и транзакции.

~14 мин чтения

Введение в MongoDB: CRUD, BSON и схемы данных

MongoDB — документоориентированная СУБД: данные хранятся как BSON-документы (бинарный JSON с типами Date, ObjectId, Decimal128 и т.д.) в коллекциях. Один документ может иметь другой набор полей, чем соседний — гибкость ценой дисциплины на уровне приложения и схемы.

Этот гайд закрывает базу: подключение, CRUD, фильтры и выбор модели вложение vs ссылки. Индексы и aggregation — в «Индексы и агрегации в MongoDB»; репликация и шарды — в «Репликация и шардирование MongoDB». Для сравнения с реляционным JSON см. JSONB в PostgreSQL.


1. Иерархия и термины

УровеньЧто это
КластерВ проде обычно replica set или sharded cluster
База данныхКонтейнер коллекций и пользователей
КоллекцияНабор документов
ДокументBSON-объект (лимит 16 МБ на документ в классической модели)

Поле _id уникально в коллекции. Если не задано, сервер или драйвер создаст ObjectId.


2. Установка и подключение

Официальная установка: документация MongoDB под вашу ОС или образ Docker mongo:7.

mongosh:

bash
mongosh "mongodb://localhost:27017"

Connection string в приложениях:

text
mongodb://user:pass@host1:27017,host2:27017/mydb?replicaSet=rs0&authSource=admin

Параметры authSource, replicaSet, TLS проверяйте для production.


3. CRUD: создание

javascript
use shop

db.orders.insertOne({
  customerId: "c-42",
  items: [{ sku: "A1", qty: 2, price: 9.99 }],
  status: "new",
  createdAt: new Date()
})

db.orders.insertMany([
  { customerId: "c-1", status: "new", createdAt: new Date() },
  { customerId: "c-2", status: "paid", createdAt: new Date() }
], { ordered: false })

Для идемпотентности критичных ключей часто используют стабильный _id или уникальный индекс.


4. Чтение: find и проекция

javascript
db.orders.find(
  { customerId: "c-42", status: { $in: ["new", "paid"] } },
  { items: 1, status: 1, createdAt: 1, _id: 0 }
)

db.orders.findOne({ _id: ObjectId("65a1b2c3d4e5f678901234ab") })

db.orders.find({ status: "new" }).sort({ createdAt: -1 }).limit(20)

Частые операторы фильтра

ОператорНазначение
$eq, $ne, $gt, $gte, $lt, $lteСравнения
$in, $ninСписки
$and, $or, $not, $norЛогика
$existsНаличие поля
$regexСтроки (без индекса — COLLSCAN)
$elemMatchУсловие на элемент массива
javascript
db.orders.find({
  items: { $elemMatch: { sku: "A1", qty: { $gte: 1 } } }
})

5. Обновление и замена

javascript
db.orders.updateOne(
  { _id: someId },
  {
    $set: { status: "shipped", updatedAt: new Date() },
    $inc: { "items.$[elem].qty": 1 }
  },
  { arrayFilters: [{ "elem.sku": "A1" }] }
)

db.orders.updateMany(
  { status: "new", createdAt: { $lt: new Date(Date.now() - 7*864e5) } },
  { $set: { status: "stale" } }
)

db.orders.replaceOne({ _id: someId }, { customerId: "c-9", status: "new" })

Массивы: $push, $pull, $addToSet, $[] / arrayFilters.


6. Удаление

javascript
db.orders.deleteOne({ _id: someId })
db.orders.deleteMany({ status: "cancelled", createdAt: { $lt: cutoff } })

Мягкое удаление: поле deletedAt вместо физического удаления.


7. Вложение vs ссылки

Встраивание (embedding)

Плюсы: один запрос, атомарные обновления документа. Минусы: размер документа, дублирование, сложные запросы «по всем вложенным сущностям».

Ссылки (referencing)

Плюсы: нормализация, отчёты. Минусы: несколько round-trip или $lookup, консистентность — приложение или транзакции.

Правила

  1. Вместе читается и меняется — чаще вложить.
  2. Сущность живёт сама и переиспользуется — отдельная коллекция + ссылка.
  3. Рост массива без верха и лимит 16 МБ — сигнал вынести в отдельную коллекцию.
  4. Многодокументные транзакции (с 4.0+) — проектируйте границы осознанно.

8. Валидация на сервере

javascript
db.createCollection("orders", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["customerId", "status", "createdAt"],
      properties: {
        customerId: { bsonType: "string" },
        status: { enum: ["new", "paid", "shipped", "cancelled"] },
        createdAt: { bsonType: "date" }
      }
    }
  },
  validationLevel: "strict",
  validationAction: "error"
})

9. Транзакции (кратко)

javascript
const session = db.getMongo().startSession()
session.startTransaction()
try {
  db.orders.insertOne({ ... }, { session })
  db.inventory.updateOne({ sku: "A1" }, { $inc: { qty: -1 } }, { session })
  session.commitTransaction()
} catch (e) {
  session.abortTransaction()
  throw e
} finally {
  session.endSession()
}

На sharded cluster есть ограничения и накладные расходы — см. гайд по шардированию.


10. Типичные ошибки

ОшибкаПочему больно
Нет индекса под фильтр + сортировкуCOLLSCAN
Огромные массивы в одном документеЛимит 16 МБ, дорогие обновления
$regex без якоряНе использует индекс по префиксу
«Без схемы» = без договорённостейХаос в данных
Игнорировать writeConcern / readConcernПотеря или устаревшие чтения при сбоях

Следующие шаги