Как правильно спроектировать модель данных для рецепта / ингредиента, используя MongoDB - PullRequest
0 голосов
/ 02 января 2019

Недавно я разработал модель базы данных или ERD, используя Hackalode.Итак, проблема, с которой я сейчас сталкиваюсь, заключается в том, что я опираюсь на свой текущий дизайн, и я не могу правильно запросить его, как я хотел.Я изучал ERD с помощью MYSQL и знаю, что Mongo не работает одинаково

Идея была проста, я хочу рецепт, содержащий список ингредиентов в массиве, а ингредиенты из отдельной коллекции.Рецепт также состоит из измерения ингредиента, т.е.(1 столовая ложка сахара)

Можно также запросить из списка ингредиентов и найти рецепт, который содержит ингредиенты

Я хотел, чтобы эта коллекция была во многих отношениях со многими, и в рецепте могут использоваться ингредиенты.которые уже есть в базе данных.

Current design of data model

Я просто не знаю, как запросить данные

Я много пробовалспособов с помощью $ elemMatch и заполнить, и все, что я получаю, это пустой список массивов в результате.

Я ожидаю два типа запросов, где я могу запросить по названию ингредиентов или по рецепту

Мой результат ожидания будет таким:

[{
   id: ...,
   name: ....,
   description: ...,
   macros: [...],
   ingredients: [
   {
       id,
       amount: ....,
       unit: ....
       ingredient: {
           id: ....,
           name: ....
       }
   }
}, { ... }]

Но вместо получения

[]

1 Ответ

0 голосов
/ 02 января 2019

Имхо, ваш дизайн совершенно неверный.Вы слишком нормализовали свои данные.Я хотел бы сделать что-то гораздо проще и использовать встраивание.Причиной этого является то, что вы определяете ваши варианты использования сначала , а затем моделируете свои данные, чтобы наиболее эффективно ответить на вопрос, возникающий из ваших вариантов использования.

Предполагаемые варианты использования

  1. Как пользователь, я хочу получить список всех рецептов.
  2. Как пользователь, я хочу получить список всех рецептов по ингредиентам.
  3. Как дизайнер, яхочу показать список всех ингредиентов.
  4. Как пользователь, я хочу иметь возможность ссылаться на рецепты составных ингредиентов, если они присутствуют на сайте.

Конечно, это лишь небольшая выдержка, но для этого примера достаточно.

Как ответить на вопросы

Хорошо, первый из них чрезвычайно прост:

db.recipes.find()[.limit()[.skip()]]

Теперь, как мы можем найти ингредиент?Простой ответ: создайте текстовый индекс по именам ингредиентов (и, возможно, по некоторым другим полям, , поскольку у вас может быть только один текстовый индекс на коллекцию . Тогда запрос одинаково прост:

db.recipes.find({$text:{$search:"ingredient name"}})

«Эй, подожди минутку! Как мне получить список всех ингредиентов?» Давайте предположим, что нам нужен простой список ингредиентов с номером того, как часто они используются на самом деле:

db.recipes.aggregate([
  // We want all ingredients as single values
  {$unwind:"$Ingredients"},
  // We want the response to be "Ingredient"
  {$project:{_id:0,"Ingredient":"$Ingredients.Name"}
  // We count the occurrence of each ingredient
  // in the recipes
  {$group:{_id:"$Ingredient",count:{$sum:1}}}
])

На самом деле этого было бы достаточно, если у вас нет базы данных с миллиардами рецептов. В этом случае вы, возможно, захотите взглянуть на возрастающую карту / уменьшить вместо агрегации. Подсказка: выследует добавить временную метку к рецептам, чтобы иметь возможность использовать инкрементную карту / уменьшение.

Если у вас есть пара сотен К или пара миллионов рецептов, вы также можете добавить $out этап предварительной агрегации ваших данных.

По измерениям

Имхо, нет смысла определять измерения. Есть чайные, столовые, метрические иImperiВсе измерения, группы, такие как «дюжина» или спецификации, такие как «гвоздика».Который вы действительно не хотите конвертировать друг в друга или даже установить на ограниченное количество измерений.Сколько унций зубчик чеснока?;)

Итог: сделайте его свободным текстовым полем, возможно, с некоторыми предложениями автозаполнения.

Пересмотренная модель данных

Рецепт

{
  _id: new ObjectId(),
  Name: "Surf & Turf Kebap",
  Ingredients: [
    {
      Name: "Flunk Steak",
      Measurement: "200 g"
    },
    { 
      Name: "Prawns",
      Measurement: "300g",
      Note: "Fresh ones!"
    },
    {
      Name: "Garlic Oil",
      Measurement: "1 Tablespoon",
      Link: "/recipes/5c2cc4acd98df737db7c5401"
    }
  ]
}

ИПример текстового индекса:

db.recipes.createIndex({Name:"text","Ingredients.Name":"text"})

Теория, лежащая в его основе

Рецепт - это ваша базовая структура данных, поскольку ваше приложение должно хранить и предоставлять их, возможно, на основе определенных критериев,Ингредиенты и измерения (в той степени, в которой это имеет смысл) могут быть легко получены из рецептов.Так зачем же хранить ингредиенты и измерения самостоятельно.Это только усложняет вашу модель данных, но не дает никаких преимуществ.

hth

...