Проектирование базы данных при отсутствии запроса на присоединение В MongoDB: как использовать ссылки на документы в других коллекциях
Я вмешиваюсь в MongoDB, чтобы увидеть, как это будет в реальном проекте в качестве платформы для серьезного хранения данных, но у меня есть небольшая проблема с пониманием концепции, или, скорее, как должен выглядеть дизайн данных. Я понимаю идею встраивания объектов, например, вместо того, что бы вы делали в СУБД, например:
PEOPLE (Table):
id | Name
---------
1 | John
2 | Steve
PHONES (Table):
id | peopleId | phone
1 | 1 | 555 66 77
2 | 1 | 555 66 78
3 | 2 | 555 11 22
В MongoDB вы должны создать два документа в коллекции, встраивающие подробные объекты, например:
{
name: "John",
phones: [
{phone: "555 66 77"},
{phone: "555 66 78"}
]
},
{
name: "Steve",
phones: [
{phone: "555 11 22"}
]
}
Теперь этот подход хорош, и он будет работать там, где детальные объекты довольно уникальны для каждого мастер-документа (так как в каждом телефоне принадлежит только одному человеку), но как только вы попадете в область (да!) Отношений, где детали не являются строго уникальные для своих владельцев, вы попадете в беду. Косидер отношения Книга / Автор. Существует много книг, в которых может быть более одного автора, в то время как у многих авторов будет более одной книги (отношение многих ко многим). Если вы встраиваете документы автора в книги, вам придется дублировать данные автора столько раз, сколько у него книг. То же самое, наоборот, если вы встраиваете книги в авторов, вы будете иметь повторяющиеся данные столько раз, сколько в этой книге есть авторы, то есть эта же книга будет появляться в документах других авторов. Само собой разумеется, это создает адские проблемы согласованности данных.
{
book: "A Nice Title",
authors: [
{name: "Jane", age: 30},
{name: "Tom", age: 20}
]
},
{
book: "Some Other Nice Title",
authors: [
{name: "Jane", age: 29},
{name: "Tom", age: 21}
]
}
Как здесь, Джейн 30 или 29 лет?
Теперь, как я понимаю из прочтения, предпочтительным способом решения этой проблемы является сохранение поддокумента в его собственной коллекции и использование его _id вместо встраивания (кто-нибудь чувствует, что мы вернулись к РСУБД на данном этапе?), либо сделав это вручную, и запросив каждый документ, который вы получите, для получения подробной информации (в результате вы получите множество запросов для каждого документа и, если у вас есть список документов, умножьте его на количество документов!) или используйте DBRef, которые, как говорят, выполняют То же самое на уровне DRIVER, в отличие от уровня сервера, что означает то же самое, только не мной, а драйвером, поэтому применяется тот же номер запроса, а именно: нагрузка на сеть, нагрузка на сервер, ожидание, ожидание... Вот пример:
people:
{
_id: 1,
name: "John",
phones: [
{phones_id: 1},
{phones_id: 2}
]
},
{
_id: 2,
name: "Steve",
phones: [
{phone_id: 3}
]
}
телефоны:
{
_id: 1,
phone: "555 66 77"
},
{
_id: 2,
phone: "555 66 78"
},
{
_id: 3,
phone: "555 11 22"
}
Это означает, что после первого запроса, из которого я получаю документы людей, мне нужно будет выполнить еще 3 запроса по коллекции телефонов, чтобы фактические телефоны составили список телефонных номеров людей.
Сразу же при сегодняшней загрузке данных я могу сказать вам: это НЕ полетит. Представьте себе, что это был 50000 длинный список книг с 10 авторами каждый? Я НЕ отправляю 500,001 запросов на сервер, чтобы получить только один список.
Конструирует как:
bookLinks:
{ bookId: 1, authorId: 1}, {bookId: 2, authorId: 1}...
только усугубит ситуацию: теперь вам нужно выполнить один запрос для ссылок, равное количество запросов в качестве итоговых документов, чтобы связать их с книгами, а затем несколько запросов для авторов, что приведет к 550,001 запросам (для 50000 книг с 10 авторами каждый).
Итак... Поскольку любой реальный проект, очевидно, будет иметь как встраиваемые (телефонная книга), так и не встраиваемые (авторы / книги) модели, и поскольку MongoDB не может на уровне сервера разрешать dbrefs для документов в других коллекциях и встраивать их, каков этот путь? идти? Каков правильный или предпочтительный способ разработки коллекций документов в таком сценарии?
Я надеюсь, что мне удалось описать мои проблемы достаточно точно.
Примечание: пожалуйста, не советуйте локальному кешированию деталей, чтобы уменьшить количество запросов (нет: я все равно не буду отправлять 300,001 запросов на сервер вместо 500,001). Такие методы будут исправлениями для плохого дизайна, они не решат проблему.
1 ответ
В некоторых случаях вы можете сделать это с помощью конвейера агрегации и его $lookup
оператор. Примерно так (извините, пример документа, а не ваш пример)
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
По сути, это лево-внешнее объединение с платой (более сложный синтаксис запросов). В вашем случае вам, возможно, придется $unwind
ваши массивы в первую очередь.
Кроме того, вы можете использовать несколько поисков?
Да, это должно быть так же просто, как разместить несколько $lookup
шаги в трубопроводе.