Skip to content

Instantly share code, notes, and snippets.

@dmitry-osin
Created May 1, 2025 13:55
Show Gist options
  • Save dmitry-osin/2ba280c50919eb58b08a9b792e90c735 to your computer and use it in GitHub Desktop.
Save dmitry-osin/2ba280c50919eb58b08a9b792e90c735 to your computer and use it in GitHub Desktop.
Шпаргалка по MongoDB

Шпаргалка по MongoDB

MongoDB - это документоориентированная NoSQL база данных, которая хранит данные в формате BSON (бинарный JSON). Вот полная шпаргалка по основным концепциям и командам MongoDB.

Основные концепции

Структура данных

  • База данных - контейнер для коллекций
  • Коллекция - группа документов (аналог таблицы в SQL)
  • Документ - набор пар ключ-значение в формате BSON (аналог строки в SQL)
  • Поле - пара ключ-значение в документе (аналог столбца в SQL)
  • Индекс - специальная структура данных для ускорения запросов

Типы данных

  • Строки (String)
  • Числа (Number, NumberInt, NumberLong, NumberDecimal)
  • Булевы значения (Boolean)
  • Массивы (Array)
  • Объекты (Object)
  • Null (null)
  • Дата/время (Date)
  • ObjectId (ObjectId) - уникальный идентификатор документа
  • Двоичные данные (BinData)
  • Регулярные выражения (RegExp)

Подключение и базовые операции

Подключение

# Локальное подключение
mongo

# Подключение к удаленному серверу
mongo "mongodb://hostname:port/database"

# Подключение с аутентификацией
mongo "mongodb://username:password@hostname:port/database"

Работа с базами данных

// Показать все базы данных
show dbs

// Создать/переключиться на базу данных
use mydatabase

// Показать текущую базу данных
db

// Удалить базу данных
db.dropDatabase()

Работа с коллекциями

// Показать все коллекции в текущей БД
show collections

// Создать коллекцию
db.createCollection("users")

// Удалить коллекцию
db.users.drop()

CRUD операции

Create (Создание)

// Вставить один документ
db.users.insertOne({
  name: "Иван",
  age: 30,
  email: "[email protected]"
})

// Вставить несколько документов
db.users.insertMany([
  { name: "Мария", age: 25, email: "[email protected]" },
  { name: "Алексей", age: 35, email: "[email protected]" }
])

// Альтернативный синтаксис (устаревший)
db.users.insert({ name: "Петр", age: 40 })

Read (Чтение)

// Найти все документы в коллекции
db.users.find()

// Найти с форматированием вывода
db.users.find().pretty()

// Найти по условию
db.users.find({ age: 30 })

// Найти по нескольким условиям (AND)
db.users.find({ age: 30, name: "Иван" })

// Найти один документ
db.users.findOne({ name: "Иван" })

// Ограничение полей в результате (1 - включить, 0 - исключить)
db.users.find({}, { name: 1, email: 1, _id: 0 })

// Ограничение количества результатов
db.users.find().limit(5)

// Пропуск результатов (для пагинации)
db.users.find().skip(10).limit(5)

// Сортировка (1 - по возрастанию, -1 - по убыванию)
db.users.find().sort({ age: 1, name: -1 })

// Подсчет документов
db.users.countDocuments({ age: { $gt: 30 } })

Update (Обновление)

// Обновить один документ
db.users.updateOne(
  { name: "Иван" },
  { $set: { age: 31, status: "активен" } }
)

// Обновить несколько документов
db.users.updateMany(
  { age: { $lt: 30 } },
  { $set: { status: "молодой" } }
)

// Заменить весь документ
db.users.replaceOne(
  { name: "Иван" },
  { name: "Иван Петров", age: 31, email: "[email protected]" }
)

// Инкремент числового поля
db.users.updateOne(
  { name: "Иван" },
  { $inc: { age: 1 } }
)

// Добавить элемент в массив
db.users.updateOne(
  { name: "Иван" },
  { $push: { hobbies: "футбол" } }
)

// Удалить элемент из массива
db.users.updateOne(
  { name: "Иван" },
  { $pull: { hobbies: "футбол" } }
)

// Upsert (создать если не существует)
db.users.updateOne(
  { name: "Новый пользователь" },
  { $set: { age: 25 } },
  { upsert: true }
)

Delete (Удаление)

// Удалить один документ
db.users.deleteOne({ name: "Иван" })

// Удалить несколько документов
db.users.deleteMany({ age: { $lt: 18 } })

// Удалить все документы
db.users.deleteMany({})

Операторы запросов

Операторы сравнения

// Равно (=)
db.users.find({ age: 30 })

// Не равно (!=)
db.users.find({ age: { $ne: 30 } })

// Больше чем (>)
db.users.find({ age: { $gt: 30 } })

// Больше или равно (>=)
db.users.find({ age: { $gte: 30 } })

// Меньше чем (<)
db.users.find({ age: { $lt: 30 } })

// Меньше или равно (<=)
db.users.find({ age: { $lte: 30 } })

// В списке значений (IN)
db.users.find({ age: { $in: [25, 30, 35] } })

// Не в списке значений (NOT IN)
db.users.find({ age: { $nin: [25, 30, 35] } })

Логические операторы

// И (AND) - можно также использовать запятую между условиями
db.users.find({ $and: [{ age: { $gt: 25 } }, { age: { $lt: 35 } }] })

// ИЛИ (OR)
db.users.find({ $or: [{ age: { $lt: 20 } }, { age: { $gt: 60 } }] })

// НЕ (NOT)
db.users.find({ age: { $not: { $gt: 30 } } })

// И НЕ (NOR)
db.users.find({ $nor: [{ age: 20 }, { name: "Иван" }] })

Операторы для элементов

// Проверка существования поля
db.users.find({ email: { $exists: true } })

// Проверка типа поля
db.users.find({ age: { $type: "number" } })
db.users.find({ age: { $type: 16 } }) // 16 - код для типа Int32

Операторы для массивов

// Точное совпадение массива
db.users.find({ hobbies: ["чтение", "спорт", "музыка"] })

// Содержит элемент
db.users.find({ hobbies: "спорт" })

// Содержит все элементы
db.users.find({ hobbies: { $all: ["спорт", "музыка"] } })

// Размер массива
db.users.find({ hobbies: { $size: 3 } })

// Поиск по элементам массива с условием
db.users.find({ "scores.0": { $gt: 80 } }) // Первый элемент > 80
db.users.find({ scores: { $elemMatch: { $gt: 80, $lt: 90 } } }) // Есть элемент > 80 и < 90

Операторы для обновления

// Установить значение
db.users.updateOne({ name: "Иван" }, { $set: { age: 31 } })

// Удалить поле
db.users.updateOne({ name: "Иван" }, { $unset: { status: "" } })

// Увеличить/уменьшить число
db.users.updateOne({ name: "Иван" }, { $inc: { age: 1 } })

// Умножить число
db.users.updateOne({ name: "Иван" }, { $mul: { salary: 1.1 } }) // Увеличить на 10%

// Минимальное значение (обновляет только если новое значение меньше)
db.users.updateOne({ name: "Иван" }, { $min: { age: 25 } })

// Максимальное значение (обновляет только если новое значение больше)
db.users.updateOne({ name: "Иван" }, { $max: { age: 35 } })

// Переименовать поле
db.users.updateMany({}, { $rename: { "old_field": "new_field" } })

// Добавить в массив
db.users.updateOne({ name: "Иван" }, { $push: { hobbies: "футбол" } })

// Добавить несколько значений в массив
db.users.updateOne(
  { name: "Иван" },
  { $push: { hobbies: { $each: ["футбол", "теннис"] } } }
)

// Добавить уникальные значения в массив
db.users.updateOne(
  { name: "Иван" },
  { $addToSet: { hobbies: "футбол" } }
)

// Удалить из массива
db.users.updateOne({ name: "Иван" }, { $pull: { hobbies: "футбол" } })

// Удалить первый или последний элемент массива
db.users.updateOne({ name: "Иван" }, { $pop: { hobbies: 1 } }) // Последний элемент
db.users.updateOne({ name: "Иван" }, { $pop: { hobbies: -1 } }) // Первый элемент

Индексы

Создание индексов

// Создать простой индекс
db.users.createIndex({ name: 1 }) // 1 - по возрастанию, -1 - по убыванию

// Создать составной индекс
db.users.createIndex({ name: 1, age: -1 })

// Создать уникальный индекс
db.users.createIndex({ email: 1 }, { unique: true })

// Создать индекс с ограниченным временем жизни (TTL)
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 }) // 1 час

// Создать текстовый индекс
db.articles.createIndex({ title: "text", content: "text" })

// Создать геопространственный индекс
db.places.createIndex({ location: "2dsphere" })

// Создать частичный индекс
db.users.createIndex(
  { email: 1 },
  { partialFilterExpression: { age: { $gt: 18 } } }
)

// Создать индекс с весами для текстового поиска
db.articles.createIndex(
  { title: "text", content: "text" },
  { weights: { title: 10, content: 1 } }
)

Управление индексами

// Показать все индексы
db.users.getIndexes()

// Удалить индекс по имени
db.users.dropIndex("name_1")

// Удалить все индексы (кроме _id)
db.users.dropIndexes()

// Перестроить индексы
db.users.reIndex()

Агрегации

Стадии агрегации

// Базовый пример агрегации
db.orders.aggregate([
  { $match: { status: "completed" } },
  { $group: { _id: "$customer", totalAmount: { $sum: "$amount" } } },
  { $sort: { totalAmount: -1 } }
])

// $match - фильтрация документов (как find)
db.users.aggregate([
  { $match: { age: { $gt: 25 } } }
])

// $project - выбор полей (как проекция в find)
db.users.aggregate([
  { $project: { name: 1, email: 1, _id: 0 } }
])

// $group - группировка документов
db.orders.aggregate([
  { $group: { 
      _id: "$customer", 
      count: { $sum: 1 },
      totalAmount: { $sum: "$amount" },
      avgAmount: { $avg: "$amount" },
      minAmount: { $min: "$amount" },
      maxAmount: { $max: "$amount" }
    } 
  }
])

// $sort - сортировка
db.users.aggregate([
  { $sort: { age: -1, name: 1 } }
])

// $limit - ограничение количества
db.users.aggregate([
  { $limit: 10 }
])

// $skip - пропуск документов
db.users.aggregate([
  { $skip: 10 }
])

// $unwind - развертывание массива
db.users.aggregate([
  { $unwind: "$hobbies" }
])

// $lookup - объединение коллекций (JOIN)
db.orders.aggregate([
  { $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "userDetails"
    }
  }
])

// $addFields - добавление новых полей
db.users.aggregate([
  { $addFields: { 
      fullName: { $concat: ["$firstName", " ", "$lastName"] },
      isAdult: { $gte: ["$age", 18] }
    } 
  }
])

// $count - подсчет документов
db.users.aggregate([
  { $match: { age: { $gt: 30 } } },
  { $count: "adultsCount" }
])

// $facet - параллельные агрегации
db.users.aggregate([
  { $facet: {
      "byAge": [
        { $group: { _id: "$age", count: { $sum: 1 } } }
      ],
      "byCity": [
        { $group: { _id: "$city", count: { $sum: 1 } } }
      ]
    }
  }
])

// $out - сохранение результата в новую коллекцию
db.orders.aggregate([
  { $group: { _id: "$customer", total: { $sum: "$amount" } } },
  { $out: "customerTotals" }
])

// $merge - слияние результата с существующей коллекцией
db.orders.aggregate([
  { $group: { _id: "$customer", total: { $sum: "$amount" } } },
  { $merge: {
      into: "customerStats",
      on: "_id",
      whenMatched: "merge",
      whenNotMatched: "insert"
    }
  }
])

Операторы агрегации

// Арифметические операторы
db.products.aggregate([
  { $project: {
      name: 1,
      discountPrice: { $subtract: ["$price", "$discount"] },
      priceWithTax: { $multiply: ["$price", 1.2] },
      pricePerUnit: { $divide: ["$price", "$quantity"] },
      totalCost: { $add: ["$price", "$shipping"] }
    }
  }
])

// Строковые операторы
db.users.aggregate([
  { $project: {
      name: 1,
      nameLower: { $toLower: "$name" },
      nameUpper: { $toUpper: "$name" },
      nameLength: { $strLenCP: "$name" },
      firstChar: { $substr: ["$name", 0, 1] },
      fullName: { $concat: ["$firstName", " ", "$lastName"] }
    }
  }
])

// Логические операторы
db.users.aggregate([
  { $project: {
      name: 1,
      age: 1,
      isAdult: { $gte: ["$age", 18] },
      ageCategory: {
        $cond: {
          if: { $lt: ["$age", 18] },
          then: "ребенок",
          else: {
            $cond: {
              if: { $lt: ["$age", 65] },
              then: "взрослый",
              else: "пенсионер"
            }
          }
        }
      }
    }
  }
])

// Операторы для дат
db.orders.aggregate([
  { $project: {
      orderDate: 1,
      year: { $year: "$orderDate" },
      month: { $month: "$orderDate" },
      day: { $dayOfMonth: "$orderDate" },
      hour: { $hour: "$orderDate" },
      dayOfWeek: { $dayOfWeek: "$orderDate" }, // 1 = воскресенье, 7 = суббота
      daysSinceOrder: {
        $divide: [
          { $subtract: [new Date(), "$orderDate"] },
          1000 * 60 * 60 * 24
        ]
      }
    }
  }
])

// Операторы для массивов
db.users.aggregate([
  { $project: {
      name: 1,
      hobbies: 1,
      hobbyCount: { $size: "$hobbies" },
      firstHobby: { $arrayElemAt: ["$hobbies", 0] },
      lastHobby: { $arrayElemAt: ["$hobbies", -1] },
      hasReading: { $in: ["чтение", "$hobbies"] },
      allHobbies: { $reduce: {
        input: "$hobbies",
        initialValue: "",
        in: { $concat: ["$$value", { $cond: [{ $eq: ["$$value", ""] }, "", ", "] }, "$$this"] }
      }}
    }
  }
])

Транзакции

MongoDB поддерживает транзакции с версии 4.0 для реплика-сетов и с версии 4.2 для шардированных кластеров.

// Начать сессию
const session = db.getMongo().startSession();

// Начать транзакцию
session.startTransaction();

try {
  // Операции в транзакции
  const usersCollection = session.getDatabase("mydatabase").users;
  const ordersCollection = session.getDatabase("mydatabase").orders;
  
  usersCollection.updateOne(
    { _id: userId },
    { $inc: { balance: -100 } }
  );
  
  ordersCollection.insertOne({
    userId: userId,
    amount: 100,
    status: "completed",
    date: new Date()
  });
  
  // Если все операции успешны, фиксируем транзакцию
  session.commitTransaction();
} catch (error) {
  // При ошибке откатываем транзакцию
  session.abortTransaction();
  print("Ошибка транзакции:", error);
} finally {
  // Закрываем сессию
  session.endSession();
}

Администрирование

Управление пользователями

// Создать пользователя
db.createUser({
  user: "admin",
  pwd: "password",
  roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
})

// Добавить роль пользователю
db.grantRolesToUser("admin", [{ role: "readWrite", db: "mydatabase" }])

// Удалить роль у пользователя
db.revokeRolesFromUser("admin", [{ role: "readWrite", db: "mydatabase" }])

// Изменить пароль пользователя
db.changeUserPassword("admin", "newpassword")

// Удалить пользователя
db.dropUser("admin")

// Показать всех пользователей
db.getUsers()

// Показать текущего пользователя
db.runCommand({ connectionStatus: 1 })

Роли в MongoDB

  • Встроенные роли для базы данных:

    • read - чтение данных
    • readWrite - чтение и запись данных
    • dbAdmin - административные операции без доступа к данным
    • userAdmin - управление пользователями и ролями
    • dbOwner - комбинация readWrite, dbAdmin и userAdmin
  • Встроенные роли для всех баз данных:

    • readAnyDatabase - чтение любой базы данных
    • readWriteAnyDatabase - чтение и запись в любую базу данных
    • userAdminAnyDatabase - управление пользователями в любой базе данных
    • dbAdminAnyDatabase - администрирование любой базы данных
    • root - полный доступ ко всему

Мониторинг и статистика

// Статистика базы данных
db.stats()

// Статистика коллекции
db.users.stats()

// Информация о выполнении запроса
db.users.find({ age: { $gt: 30 } }).explain("executionStats")

// Текущие операции
db.currentOp()

// Убить операцию по ID
db.killOp(opId)

// Статус сервера
db.serverStatus()

// Информация о профилировании
db.getProfilingStatus()

// Настройка профилирования (0 - выкл, 1 - медленные запросы, 2 - все запросы)
db.setProfilingLevel(1, { slowms: 100 })

// Просмотр профилированных запросов
db.system.profile.find().sort({ ts: -1 }).limit(10)

Резервное копирование и восстановление

# Создание резервной копии
mongodump --db=mydatabase --out=/backup/mongo

# Восстановление из резервной копии
mongorestore --db=mydatabase /backup/mongo/mydatabase

# Экспорт коллекции в JSON
mongoexport --db=mydatabase --collection=users --out=users.json

# Импорт из JSON
mongoimport --db=mydatabase --collection=users --file=users.json

Шардинг и репликация

Репликация

// Инициализация реплика-сета
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "mongodb0.example.net:27017" },
    { _id: 1, host: "mongodb1.example.net:27017" },
    { _id: 2, host: "mongodb2.example.net:27017" }
  ]
})

// Статус реплика-сета
rs.status()

// Добавить узел в реплика-сет
rs.add("mongodb3.example.net:27017")

// Удалить узел из реплика-сета
rs.remove("mongodb3.example.net:27017")

// Настроить узел как арбитр (не хранит данные)
rs.addArb("mongodb4.example.net:27017")

// Настройка приоритета узла
cfg = rs.conf()
cfg.members[1].priority = 2
rs.reconfig(cfg)

// Принудительное переключение мастера
rs.stepDown()

Шардинг

// Включение шардинга для базы данных
sh.enableSharding("mydatabase")

// Создание шардированного ключа для коллекции
sh.shardCollection("mydatabase.users", { "userId": "hashed" })

// Статус шардинга
sh.status()

// Добавление шарда
sh.addShard("rs1/mongodb0.example.net:27017")

// Удаление шарда
sh.removeShard("rs1")

// Балансировка шардов
sh.startBalancer()
sh.stopBalancer()
sh.isBalancerRunning()

Дополнительные возможности

Геопространственные запросы

// Создание 2dsphere индекса
db.places.createIndex({ location: "2dsphere" })

// Поиск мест в радиусе 1000 метров от точки
db.places.find({
  location: {
    $near: {
      $geometry: {
        type: "Point",
        coordinates: [55.7558, 37.6173] // [долгота, широта]
      },
      $maxDistance: 1000
    }
  }
})

// Поиск мест внутри полигона
db.places.find({
  location: {
    $geoWithin: {
      $geometry: {
        type: "Polygon",
        coordinates: [
          [
            [55.75, 37.61],
            [55.75, 37.62],
            [55.76, 37.62],
            [55.76, 37.61],
            [55.75, 37.61]
          ]
        ]
      }
    }
  }
})

// Поиск мест, пересекающихся с полигоном
db.places.find({
  location: {
    $geoIntersects: {
      $geometry: {
        type: "Polygon",
        coordinates: [/* ... */]
      }
    }
  }
})

Текстовый поиск

// Создание текстового индекса
db.articles.createIndex({ title: "text", content: "text" })

// Простой текстовый поиск
db.articles.find({ $text: { $search: "mongodb nosql" } })

// Текстовый поиск с весами в результатах
db.articles.find(
  { $text: { $search: "mongodb nosql" } },
  { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })

// Поиск точной фразы
db.articles.find({ $text: { $search: "\"mongodb database\"" } })

// Исключение слов из поиска
db.articles.find({ $text: { $search: "mongodb -sql" } })

Валидация схемы

// Создание коллекции с валидацией
db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email", "age"],
      properties: {
        name: {
          bsonType: "string",
          description: "Должно быть строкой и обязательно"
        },
        email: {
          bsonType: "string",
          pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
          description: "Должен быть валидным email адресом"
        },
        age: {
          bsonType: "int",
          minimum: 18,
          maximum: 120,
          description: "Должно быть целым числом от 18 до 120"
        },
        phone: {
          bsonType: "string",
          description: "Должно быть строкой, если указано"
        }
      }
    }
  },
  validationLevel: "strict", // strict или moderate
  validationAction: "error" // error или warn
})

// Изменение правил валидации
db.runCommand({
  collMod: "users",
  validator: { /* новая схема */ },
  validationLevel: "moderate",
  validationAction: "warn"
})

Изменяемые документы (Change Streams)

// Открыть поток изменений для коллекции
const changeStream = db.users.watch();

// Итерация по изменениям
while (!changeStream.isExhausted()) {
  if (changeStream.hasNext()) {
    const change = changeStream.next();
    printjson(change);
  }
}

// Поток изменений с фильтрацией
const pipeline = [
  { $match: { "operationType": { $in: ["insert", "update"] } } },
  { $match: { "fullDocument.age": { $gte: 18 } } }
];
const filteredStream = db.users.watch(pipeline);

// Возобновление потока изменений с определенного токена
const resumeToken = changeStream.getResumeToken();
const newStream = db.users.watch([], { resumeAfter: resumeToken });

Сжатие данных

// Создание коллекции со сжатием
db.createCollection("logs", {
  storageEngine: {
    wiredTiger: {
      configString: "block_compressor=zlib"
    }
  }
})

// Доступные алгоритмы сжатия:
// - none (без сжатия)
// - snappy (по умолчанию, быстрое сжатие)
// - zlib (лучшее сжатие, но медленнее)
// - zstd (с MongoDB 4.2, хороший баланс)

Ограниченные коллекции (Capped Collections)

// Создание ограниченной коллекции (с лимитом размера)
db.createCollection("logs", { 
  capped: true, 
  size: 1048576, // 1MB максимальный размер
  max: 1000 // максимальное количество документов
})

// Проверка, является ли коллекция ограниченной
db.logs.isCapped()

// Преобразование обычной коллекции в ограниченную
db.runCommand({ convertToCapped: "users", size: 10000000 })

// Запрос в обратном порядке (от новых к старым)
db.logs.find().sort({ $natural: -1 })

// Запрос с ожиданием новых документов (как tail -f)
cursor = db.logs.find().addOption(2) // tailable cursor
while (cursor.hasNext()) {
  printjson(cursor.next())
}

Сетевые настройки и безопасность

// Проверка статуса аутентификации
db.runCommand({ getParameter: 1, authenticationMechanisms: 1 })

// Включение только локальных подключений
// В файле mongod.conf:
// net:
//   bindIp: 127.0.0.1

// Настройка SSL/TLS
// В файле mongod.conf:
// net:
//   ssl:
//     mode: requireSSL
//     PEMKeyFile: /path/to/mongodb.pem
//     CAFile: /path/to/ca.pem

// Просмотр сетевых настроек
db.adminCommand({ getParameter: 1, sslMode: 1 })

Управление памятью и кешем

// Настройка использования памяти WiredTiger
// В файле mongod.conf:
// storage:
//   wiredTiger:
//     engineConfig:
//       cacheSizeGB: 2

// Проверка текущих настроек кеша
db.serverStatus().wiredTiger.cache

// Статистика использования памяти
db.serverStatus().mem

GridFS (хранение больших файлов)

// Создание GridFS бакета
let bucket = new GridFSBucket(db, {
  bucketName: 'files'
});

// Загрузка файла
const fs = require('fs');
const fileId = bucket.openUploadStream('example.pdf', {
  metadata: { type: 'pdf', description: 'Пример документа' }
});
fs.createReadStream('/path/to/example.pdf').pipe(fileId);

// Скачивание файла
bucket.openDownloadStreamByName('example.pdf')
  .pipe(fs.createWriteStream('/path/to/save/example.pdf'));

// Поиск файлов
db.files.files.find({ "metadata.type": "pdf" })

// Удаление файла
bucket.delete(fileId);

Оптимизация производительности

Анализ запросов

// Простой анализ запроса
db.users.find({ age: { $gt: 30 } }).explain()

// Детальный анализ выполнения
db.users.find({ age: { $gt: 30 } }).explain("executionStats")

// Полный анализ с планами запросов
db.users.find({ age: { $gt: 30 } }).explain("allPlansExecution")

// Анализ агрегации
db.users.aggregate([
  { $match: { age: { $gt: 30 } } },
  { $group: { _id: "$city", count: { $sum: 1 } } }
], { explain: true })

Советы по оптимизации

  1. Индексы:

    • Создавайте индексы для часто используемых полей в запросах
    • Используйте составные индексы для запросов с несколькими условиями
    • Избегайте создания лишних индексов (они замедляют вставку и обновление)
  2. Запросы:

    • Используйте проекцию для выборки только нужных полей
    • Ограничивайте результаты с помощью limit()
    • Используйте покрывающие индексы (все поля запроса в индексе)
  3. Структура данных:

    • Денормализуйте данные для частых операций чтения
    • Используйте вложенные документы вместо соединений
    • Избегайте больших массивов в документах
  4. Шардинг:

    • Выбирайте правильный ключ шардирования
    • Распределяйте нагрузку равномерно между шардами

Типичные ошибки и их решения

  1. Ошибка соединения:

    Error: couldn't connect to server
    

    Решение: Проверьте, что MongoDB запущена, порт доступен, и нет проблем с сетью.

  2. Ошибка аутентификации:

    Authentication failed
    

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

  3. Превышение размера документа:

    Document too large
    

    Решение: Документы в MongoDB ограничены 16MB. Разделите большие документы или используйте GridFS для файлов.

  4. Дублирование ключа:

    E11000 duplicate key error
    

    Решение: Проверьте уникальность полей перед вставкой или используйте upsert с правильными условиями.

  5. Медленные запросы: Решение: Проверьте индексы, используйте explain() для анализа, оптимизируйте запросы и структуру данных.

Полезные паттерны проектирования

  1. Схема "один-ко-многим":

    // Вариант 1: Массив в родительском документе (для небольшого количества дочерних элементов)
    {
      _id: ObjectId("123"),
      name: "Иван",
      addresses: [
        { street: "Ленина", city: "Москва" },
        { street: "Пушкина", city: "Санкт-Петербург" }
      ]
    }
    
    // Вариант 2: Ссылка на родителя в дочерних документах (для большого количества)
    // Коллекция users
    { _id: ObjectId("123"), name: "Иван" }
    
    // Коллекция addresses
    { _id: ObjectId("a1"), userId: ObjectId("123"), street: "Ленина", city: "Москва" }
    { _id: ObjectId("a2"), userId: ObjectId("123"), street: "Пушкина", city: "Санкт-Петербург" }
  2. Схема "многие-ко-многим":

    // Коллекция students
    { _id: ObjectId("s1"), name: "Иван" }
    { _id: ObjectId("s2"), name: "Мария" }
    
    // Коллекция courses
    { _id: ObjectId("c1"), name: "Математика" }
    { _id: ObjectId("c2"), name: "Физика" }
    
    // Коллекция enrollments (связующая)
    { _id: ObjectId("e1"), studentId: ObjectId("s1"), courseId: ObjectId("c1"), grade: "A" }
    { _id: ObjectId("e2"), studentId: ObjectId("s1"), courseId: ObjectId("c2"), grade: "B" }
    { _id: ObjectId("e3"), studentId: ObjectId("s2"), courseId: ObjectId("c1"), grade: "A+" }
  3. Схема для версионирования:

    // Коллекция documents
    {
      _id: ObjectId("d1"),
      title: "Документ",
      current: ObjectId("v3")
    }
    
    // Коллекция versions
    { _id: ObjectId("v1"), documentId: ObjectId("d1"), content: "Версия 1", createdAt: ISODate("2023-01-01") }
    { _id: ObjectId("v2"), documentId: ObjectId("d1"), content: "Версия 2", createdAt: ISODate("2023-01-02") }
    { _id: ObjectId("v3"), documentId: ObjectId("d1"), content: "Версия 3", createdAt: ISODate("2023-01-03") }
  4. Схема для древовидных структур:

    // Вариант 1: Родительская ссылка
    {
      _id: ObjectId("c1"),
      name: "Электроника",
      parentId: null
    }
    {
      _id: ObjectId("c2"),
      name: "Телефоны",
      parentId: ObjectId("c1")
    }
    
    // Вариант 2: Путь к предкам (для быстрого поиска всей иерархии)
    {
      _id: ObjectId("c1"),
      name: "Электроника",
      path: []
    }
    {
      _id: ObjectId("c2"),
      name: "Телефоны",
      path: [ObjectId("c1")]
    }
    {
      _id: ObjectId("c3"),
      name: "Смартфоны",
      path: [ObjectId("c1"), ObjectId("c2")]
    }
  5. Схема для временных рядов:

    // Вариант 1: Документ на каждое измерение (для детальных данных)
    {
      _id: ObjectId("m1"),
      sensorId: "temp1",
      value: 22.5,
      timestamp: ISODate("2023-01-01T12:00:00Z")
    }
    
    // Вариант 2: Массивы значений (для компактности)
    {
      _id: ObjectId("s1"),
      sensorId: "temp1",
      date: ISODate("2023-01-01"),
      readings: [
        { time: ISODate("2023-01-01T12:00:00Z"), value: 22.5 },
        { time: ISODate("2023-01-01T12:15:00Z"), value: 22.7 }
      ]
    }

Новые возможности MongoDB (последние версии)

  1. Транзакции в распределенных системах (с MongoDB 4.2)
  2. Поля с шифрованием на стороне клиента (с MongoDB 4.2)
  3. Wildcard индексы (с MongoDB 4.2):
    // Индекс для всех полей, начинающихся с "address."
    db.users.createIndex({ "address.$**": 1 })
  4. Сжатие Zstandard (с MongoDB 4.2)
  5. Распределенные транзакции (с MongoDB 4.2)
  6. Оконные функции в агрегациях (с MongoDB 5.0):
    db.sales.aggregate([
      {
        $setWindowFields: {
          partitionBy: "$region",
          sortBy: { date: 1 },
          output: {
            cumulativeSum: {
              $sum: "$amount",
              window: {
                documents: ["unbounded", "current"]
              }
            }
          }
        }
      }
    ])
  7. Временные коллекции (с MongoDB 5.0)
  8. Улучшенный шардинг (с MongoDB 5.0)
  9. Улучшенный текстовый поиск (с MongoDB 5.0)
  10. Улучшенные индексы для массивов (с MongoDB 5.0)

Заключение

MongoDB - мощная и гибкая NoSQL база данных, которая отлично подходит для работы с большими объемами неструктурированных или полуструктурированных данных. Ее документоориентированная модель позволяет разработчикам быстро создавать и изменять схемы данных, а богатый набор функций, включая агрегации, геопространственные запросы, шардинг и репликацию, делает ее подходящей для широкого спектра приложений.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment