В прошлом уроке мы выбирали между чемоданом и камерой хранения: вкладывать данные или ссылаться на них. Сегодня применим этот выбор к связям. В реляционной базе их рисуют на ER-диаграмме, как в уроке про ER-модель. В MongoDB к виду связи добавляется ещё один вопрос: сколько записей на другой стороне. Все примеры на учебной базе магазина.
Сколько на другой стороне
Представь три связи из жизни. У человека один паспорт. У мамы трое детей. У студента пять кружков, а в каждом кружке двадцать студентов. Это один к одному, один ко многим и многие ко многим.
Реляционной базе этой классификации хватает: таблицы и внешние ключи выглядят одинаково, будь детей трое или три миллиона. В MongoDB к вопросу «один или много» добавляется второй: «насколько много». Специалисты MongoDB делят «много» на три размера:
- несколько: до десятков, например отзывы на книгу или адреса покупателя;
- много: сотни и тысячи, например книги одного издательства;
- очень много: миллионы и дальше, например просмотры книг или строки журнала.
Это кардинальность связи, и от неё зависит способ хранения.
Один к одному
У покупателя один адрес доставки. Читают его всегда вместе с покупателем, отдельно он не нужен. Встраиваем:
db.customers.updateOne(
{ _id: 1 },
{ $set: { address: { city: 'Москва', street: 'Тверская, 7', zip: '125009' } } }
)
db.customers.findOne({ _id: 1 })
{
_id: 1,
name: 'Ольга',
city: 'Москва',
registered_at: ISODate('2025-11-02T00:00:00.000Z'),
address: { city: 'Москва', street: 'Тверская, 7', zip: '125009' }
}
В PostgreSQL для этого понадобилась бы отдельная таблица со ссылкой или пачка колонок address_city, address_street. В MongoDB вложенный документ и есть естественная форма такой связи.
Один ко многим: три способа
Несколько: массив внутри
Отзывы на книгу это связь «один к нескольким». Их показывают со страницей книги, и их немного. Поэтому они лежат массивом reviews прямо в книге. Мы видели это в прошлом уроке.
Много: ссылка в ребёнке
У Гоголя три книги, у плодовитого автора их может быть сотня. Книги нужны сами по себе: их ищут, сортируют, продают. Поэтому книга хранит ссылку на автора, а не автор хранит книги:
db.authors.findOne({ _id: 3 })
db.books.find({ 'author._id': 3 }, { title: 1 }).sort({ _id: 1 })
{ _id: 3, name: 'Гоголь', country: 'Россия', born: 1809 }
[
{ _id: 5, title: 'Шинель' },
{ _id: 6, title: 'Мёртвые души' },
{ _id: 9, title: 'Нос' }
]
Ссылка лежит в стороне «многих», как ссылка на родителя. Это ровно то, как устроен внешний ключ в PostgreSQL: books.author_id указывает на authors.id. Разница в том, что MongoDB не проверяет, существует ли автор с таким номером. Следить за этим будет приложение.
Есть и зеркальный вариант: хранить в авторе массив номеров его книг, book_ids: [5, 6, 9]. Так делают, когда список детей ограничен и его часто читают целиком вместе с родителем. Но каждую новую книгу придётся дописывать в два места.
Очень много: только ссылка в ребёнке
Заказы покупателя и тем более просмотры книг растут без предела. Здесь вариантов нет: никаких массивов в родителе, только ссылка в каждом дочернем документе. Так устроены заказы учебной базы: в каждом лежит customer_id.
урок из курса
Урок из курса «MongoDB с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.