В прошлом уроке мы осмотрелись в mongosh и положили в базу первый документ. С этого урока начинается кластер про CRUD: создание, чтение, обновление и удаление. Начинаем с первой буквы, со вставки. Заодно у магазина появится полная учебная база, на которой будут работать все следующие уроки.
Папка с анкетами
Представь регистратуру, где на каждого посетителя заводят бумажную анкету и кладут в папку. Анкету можно сдать одну, а можно принести пачку сразу. Бланков нет: у одного посетителя заполнен телефон, у другого ещё и адрес, и регистратура принимает обе.
В MongoDB папка это коллекция, анкета это документ, а сдать анкету значит выполнить вставку. Команд для неё две:
- insertOne вставляет один документ;
- insertMany вставляет массив документов за одно обращение к серверу.
insertOne
Добавим в магазин книгу Стругацких:
db.books.insertOne({
title: 'Понедельник начинается в субботу',
genre: 'фантастика',
price: 480,
pages: 256,
year: 1965,
author: { _id: 2, name: 'Стругацкие', country: 'Россия' },
tags: ['фантастика', 'юмор']
})
{
acknowledged: true,
insertedId: ObjectId('6ac9371c7c7353d2ce52946e')
}
Сервер ответил двумя полями. acknowledged: true значит, что он принял запись. insertedId это номер нового документа. Мы его не указывали, поэтому MongoDB придумала его сама: так устроено поле _id, ему посвящён следующий урок. У тебя номер будет другим.
Создавать коллекцию заранее не нужно. Если вставить документ в коллекцию, которой ещё нет, MongoDB создаст её на лету:
db.wishlist.insertOne({ customer_id: 1, book_id: 8, note: 'на день рождения' })
show collections
{
acknowledged: true,
insertedId: ObjectId('6ac9372130bd84f0b4184d2f')
}
authors
books
customers
orders
wishlist
У этой свободы есть оборотная сторона: опечатка в имени коллекции тоже молча создаст новую коллекцию. db.bookz.insertOne(...) не упадёт, а положит книгу в bookz.
insertMany
Когда документов много, вставлять их по одному медленно: каждый insertOne это отдельный запрос по сети. insertMany принимает массив и отправляет его разом.
db.books.insertMany([
{ title: 'Чапаев и Пустота', author: { name: 'Пелевин' } },
{ title: 'Лавр', price: 590, audio: true, narrator: 'Александр Клюквин' }
])
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ac937233570afcc64861c5e'),
'1': ObjectId('6ac937233570afcc64861c5f')
}
}
В ответе insertedIds: номер документа в массиве и его _id. Обрати внимание на сами документы. У первого нет ни цены, ни жанра, у второго есть поля audio и narrator, которых нет ни у одной другой книги. MongoDB приняла оба. Это и есть гибкая схема: структуру документа задаёт приложение, а не база. Как всё-таки навести порядок через валидацию, разберём в кластере про моделирование.
Что будет, если один документ не подошёл
У каждого документа в коллекции свой _id, двух одинаковых быть не может. Попробуем вставить трёх авторов, один из которых случайно получил номер Булгакова:
db.authors.insertMany([
{ _id: 7, name: 'Пелевин', country: 'Россия' },
{ _id: 1, name: 'Ещё один Булгаков', country: 'Россия' },
{ _id: 8, name: 'Улицкая', country: 'Россия' }
])
db.authors.find({ _id: { $gte: 7 } })
Uncaught:
MongoBulkWriteError: E11000 duplicate key error collection: test.authors index: _id_ dup key: { _id: 1 }
Result: BulkWriteResult {
insertedCount: 1,
matchedCount: 0,
modifiedCount: 0,
deletedCount: 0,
upsertedCount: 0,
upsertedIds: {},
insertedIds: { '0': 7 }
}
Write Errors: [
WriteError {
err: {
index: 1,
code: 11000,
errmsg: 'E11000 duplicate key error collection: test.authors index: _id_ dup key: { _id: 1 }',
errInfo: undefined,
op: { _id: 1, name: 'Ещё один Булгаков', country: 'Россия' }
}
}
]
[ { _id: 7, name: 'Пелевин', country: 'Россия' } ]
Код E11000 значит «такой ключ уже есть». Важнее другое: Пелевин вставился, а Улицкая нет. По умолчанию insertMany работает в режиме упорядоченной вставки: идёт по массиву сверху вниз и останавливается на первой ошибке. Всё, что до ошибки, остаётся в коллекции, всё, что после, не вставляется.
Это не транзакция. В PostgreSQL INSERT с тремя строками и одной ошибкой не вставил бы ни одной, а здесь первый документ уже лежит в базе. Как в MongoDB сделать «всё или ничего», расскажем в кластере про транзакции.
Если порядок не важен, можно попросить MongoDB вставить всё, что получится:
db.authors.insertMany(
[
{ _id: 7, name: 'Пелевин', country: 'Россия' },
{ _id: 1, name: 'Ещё один Булгаков', country: 'Россия' },
{ _id: 8, name: 'Улицкая', country: 'Россия' }
],
{ ordered: false }
)
db.authors.find({ _id: { $gte: 7 } })
Ошибка будет та же, только в insertedIds окажутся документы с номерами 0 и 2. А find покажет обоих новых авторов:
[
{ _id: 7, name: 'Пелевин', country: 'Россия' },
{ _id: 8, name: 'Улицкая', country: 'Россия' }
]
С ordered: false MongoDB пропускает документы с ошибками и вставляет остальные. На больших загрузках так ещё и быстрее: сервер вправе менять порядок операций ради скорости.
урок из курса
Урок из курса «MongoDB с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.