Связи: один-ко-многим и многие-ко-многим
Как выразить классические реляционные связи средствами документов и массивов.
В документной модели связи выражают тремя способами: встраиванием, массивом ссылок и ссылкой «со стороны многих».
Один-ко-многим: встроить массив
У статьи есть комментарии — это связь «один-ко-многим». Если комментариев немного и они читаются только вместе со статьёй, их встраивают массивом:
{
"_id": "a1",
"title": "Привет, Mongo",
"comments": [
{ "author": "Анна", "text": "Полезно!" },
{ "author": "Борис", "text": "Спасибо" }
]
}Одним запросом получаете статью со всеми комментариями. Минус — если комментариев могут быть тысячи, документ распухнет (а у документа есть предел размера — 16 МБ). Тогда переходят к ссылкам.
Один-ко-многим: ссылка со стороны «многих»
Когда «многих» много (заказы пользователя, посты автора), удобнее хранить ссылку в дочерних документах. Каждый заказ знает своего пользователя:
{ "_id": "o1", "userId": "u1", "total": 1800 }
{ "_id": "o2", "userId": "u1", "total": 500 }Все заказы пользователя находятся обычным запросом — и их число ничем не ограничено:
db.orders.find({ userId: "u1" })Многие-ко-многим: массив ссылок
Студенты и курсы: студент посещает много курсов, курс — много студентов. В SQL для этого заводят связующую таблицу. В MongoDB чаще хранят массив идентификаторов на одной из сторон:
{
"_id": "s1",
"name": "Анна",
"courseIds": ["c1", "c2", "c5"]
}Найти всех студентов курса c2 — поиск по массиву (MongoDB сама проверяет вхождение значения в массив):
db.students.find({ courseIds: "c2" })Какую сторону держать «главной» (массив у студента или у курса) — зависит от того, какие запросы чаще. Если обычно спрашивают «курсы студента» — массив у студента; если «студенты курса» — у курса. Хранить массивы с обеих сторон можно, но тогда их надо синхронизировать, а это источник ошибок.
Поиск по элементу массива — это просто
Важный момент для пришедших из SQL: фильтр { courseIds: "c2" } срабатывает, если c2 есть среди элементов массива. Никакого специального синтаксиса не нужно — MongoDB понимает массивы прозрачно. Это делает массив ссылок очень удобным инструментом.
Итог
- Один-ко-многим: встраивать массив (если «многих» немного) или ссылаться со стороны дочерних документов (если много).
- Многие-ко-многим: массив
_idна одной из сторон вместо связующей таблицы. - Поиск по значению внутри массива не требует особого синтаксиса — обычный фильтр по полю-массиву.