Проектирование базы данных при отсутствии запроса на присоединение В 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 шаги в трубопроводе.

Другие вопросы по тегам