Урок 03

MongoDB: введение

Что такое MongoDB

MongoDB — документная NoSQL-СУБД.

Особенности:

  • JSON-подобные документы (BSON).
  • Гибкая схема — не обязательно, но рекомендуется.
  • Горизонтальное масштабирование через шардирование.
  • Репликация — Replica Set.
  • Индексы — как в SQL, плюс специфичные.
  • Aggregation Pipeline — мощный язык запросов.
  • ACID транзакции с 4.0 (с ограничениями).

Лицензия: SSPL (не OSI-approved, но исходники открыты).

Термины

SQLMongoDB
databasedatabase
tablecollection
rowdocument
columnfield
primary key_id
indexindex
JOIN$lookup
GROUP BY$group
transactiontransaction (4.0+)

Ключевое: коллекция не требует схемы.

BSON

BSON — Binary JSON. Хранилище MongoDB.

Типы:

  • string, int32, int64, double.
  • Decimal128 — для денег.
  • boolean, null.
  • ObjectId — 12-байтовый ID.
  • Date — timestamp.
  • Array, Object.
  • Binary, Regex.
  • Timestamp — внутренний.

Decimal128 — единственный точный тип для денег.

Установка (Docker)

docker run -d \
    --name mongo \
    -e MONGO_INITDB_ROOT_USERNAME=admin \
    -e MONGO_INITDB_ROOT_PASSWORD=secret \
    -p 27017:27017 \
    -v mongo_data:/data/db \
    mongo:7

Docker Compose:

services:
  mongo:
    image: mongo:7
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secret
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db

volumes:
  mongo_data:

Установка (Ubuntu)

curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
    sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor

echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] \
    https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \
    sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list

sudo apt update
sudo apt install -y mongodb-org
sudo systemctl enable --now mongod

Подключение

MongoDB Shell (mongosh):

mongosh "mongodb://admin:secret@localhost:27017"

Внутри:

show dbs
use myapp
db.users.insertOne({ name: 'Алиса' })
db.users.find()

Основные команды

// Базы
show dbs
use myapp
db.dropDatabase()

// Коллекции
show collections
db.createCollection('users')
db.users.drop()

// Документы
db.users.insertOne({ ... })
db.users.insertMany([{ ... }, { ... }])
db.users.find()
db.users.findOne({ email: 'a@b.com' })
db.users.updateOne({ _id: 1 }, { $set: { name: 'X' } })
db.users.deleteOne({ _id: 1 })
db.users.countDocuments()

ObjectId

12 байт:

  • 4 байта — timestamp.
  • 5 байт — random.
  • 3 байта — counter.

Что даёт:

  • Уникален без координации.
  • Сортируется по времени создания.
  • Не угадывается.

В JSON:

{ "_id": { "$oid": "507f1f77bcf86cd799439011" } }

Генерация:

const id = new ObjectId()

Replica Set

Минимум для прода: 3 ноды.

Роли:

  • Primary — принимает запись.
  • Secondary — реплики.
  • Arbiter — только голосует, без данных (не рекомендуется).

Что даёт:

  • Автоматический failover.
  • Read preference — можно читать с secondary.
  • Durability через write concern.

Настройка (dev):

rs.initiate({
    _id: 'rs0',
    members: [
        { _id: 0, host: 'localhost:27017' },
        { _id: 1, host: 'localhost:27018' },
        { _id: 2, host: 'localhost:27019' },
    ],
})

Строка подключения:

mongodb://host1:27017,host2:27017,host3:27017/myapp?replicaSet=rs0

Write Concern

Сколько нод должны подтвердить запись:

db.users.insertOne({ ... }, { writeConcern: { w: 'majority' } })

Уровни:

  • w: 0 — без подтверждения. Быстро, но не надёжно.
  • w: 1 — только primary. По умолчанию.
  • w: 'majority' — большинство. Надёжно.
  • w: 'majority', j: true — + запись в journal.

Правило: для критичных данных — majority + j: true.

Read Preference

Откуда читать:

  • primary — только primary. Свежие данные.
  • primaryPreferred — primary, если недоступен — secondary.
  • secondary — только secondary. Может отставать.
  • secondaryPreferred — наоборот.
  • nearest — ближайший по задержке.

В строке подключения:

mongodb://...?readPreference=secondaryPreferred

Строка подключения

mongodb://user:pass@host1:27017,host2:27017/myapp
    ?replicaSet=rs0
    &readPreference=secondaryPreferred
    &w=majority
    &retryWrites=true

Параметры:

  • replicaSet — имя RS.
  • readPreference — куда читать.
  • w — write concern.
  • retryWrites — авто-retry транзиентных ошибок.
  • authSource — где искать пользователя.

Клиенты

Node.js:

npm install mongodb
# или
npm install mongoose
import { MongoClient } from 'mongodb';

const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const db = client.db('myapp');
const users = db.collection('users');

await users.insertOne({ name: 'Алиса' });

PHP:

composer require mongodb/mongodb
$client = new MongoDB\Client('mongodb://localhost:27017');
$users = $client->myapp->users;
$users->insertOne(['name' => 'Алиса']);

Python:

pip install pymongo

Mongoose (Node.js)

ODM для MongoDB:

import mongoose from 'mongoose';

const userSchema = new mongoose.Schema({
    email: { type: String, required: true, unique: true },
    name: { type: String, required: true },
    age: { type: Number, min: 0 },
}, { timestamps: true });

const User = mongoose.model('User', userSchema);

await User.create({ email: 'a@b.com', name: 'Алиса' });

Что даёт:

  • Схема в приложении.
  • Валидация.
  • Middleware (pre/post hooks).
  • Populate (для связи).

Правило: даже в MongoDB — схема нужна. Mongoose её даёт.

Laravel + MongoDB

Пакет:

composer require mongodb/laravel-mongodb
class User extends \MongoDB\Laravel\Eloquent\Model
{
    protected $connection = 'mongodb';
    protected $collection = 'users';
}

User::create(['name' => 'Алиса']);
User::where('age', '>', 18)->get();

Что даёт: знакомый Eloquent.

⚠️ Eloquent-методы работают, но не всё — JOIN-ы эмулируются.

Админ-панели

  • MongoDB Compass — официальная, GUI.
  • mongosh — CLI.
  • Studio 3T — платная, много фич.
  • Robo 3T — бесплатная.
  • NoSQLBooster — платная.

Compass — удобно для отладки запросов.

Коллекции и схемы

Схемы нет — но правила есть:

  • Валидация через $jsonSchema:
db.createCollection('users', {
    validator: {
        $jsonSchema: {
            bsonType: 'object',
            required: ['email', 'name'],
            properties: {
                email: { bsonType: 'string', pattern: '^.+@.+$' },
                name: { bsonType: 'string', maxLength: 100 },
                age: { bsonType: 'int', minimum: 0 },
            },
        },
    },
})

Что даёт: валидация на уровне БД.

Правило: в приложении — Mongoose/схема, в БД — $jsonSchema для доп. защиты.

Практика

1. Разверните MongoDB в Docker.

2. Подключитесь через mongosh.

3. Создайте базу myapp, коллекцию users.

4. Вставьте документ, найдите, обновите, удалите.

5. Создайте Replica Set из 3 нод (в Docker).

6. Подключитесь через строку с replicaSet.

7. Проверьте db.users.getIndexes().

Итог

  • MongoDB — документная NoSQL.
  • BSON — внутренний формат.
  • ObjectId — 12 байт, сортируется по времени.
  • Replica Set — 3+ ноды для прода.
  • Write Concern: majority для критичных.
  • Read Preference: primary для свежих данных.
  • Mongoose — схема в приложении.
  • $jsonSchema — валидация в БД.

Дальше — документы и коллекции.