我如何使用 MongoDB 正确设计食谱/成分的数据模型

How do i properly design a data model for Recipe / Ingredient using MongoDB

最近我使用 Hackalode 设计了一个数据库模型或 ERD。 所以我目前面临的问题是,基于我目前的设计,我无法按照我的意愿正确查询它。我用 MYSQL 研究了 ERD,并且确实知道 Mongo 的工作原理不同

这个想法很简单,我想要一个包含一系列成分列表的食谱,并且这些成分来自单独的集合。 食谱还包括成分的测量,即。 (1 汤匙糖)

也可以从配料列表中查询并找到包含该配料的食谱

我希望这个集合是多对多的关系,并且食谱可以使用数据库中已有的成分。

就是不知道怎么查询数据

我通过使用 $elemMatch 和 populate 尝试了很多方法,结果我得到的只是空数组列表。

我期待两种类型的查询,我可以通过成分名称或配方进行查询

我的预期结果是这样的

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

但不是得到

[]

恕我直言,你的设计完全错误。您过度规范化了数据。我会做一些更简单的事情并使用嵌入。其背后的原因是,您先定义用例,然后对数据建模,以最有效的方式回答用例产生的问题。

假设的用例

  1. 作为用户,我想要所有食谱的列表。
  2. 作为用户,我想要一份按成分列出的所有食谱的列表。
  3. 作为一名设计师,我希望能够展示所有成分的清单。
  4. 作为用户,我希望能够 link 复合成分的食谱,如果它出现在网站上的话。

当然,这只是一小段摘录,但对于这个例子来说已经足够了。

如何回答问题

好的,第一个非常简单:

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

现在,我们如何按成分查找?简单答案:对成分名称(可能还有其他一些字段,as you can only have one text index per collection)做一个文本索引。然后,查询同样简单:

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

"Hey, wait a moment! How do I get a list of all ingredients?" 让我们假设我们想要一个简单的成分列表,以及实际使用频率的数字:

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}}}
])

这实际上就足够了,除非您有一个包含大量食谱的数据库。在这种情况下,您可能希望深入了解 incremental map/reduce 而不是聚合。 提示:您应该为食谱添加时间戳,以便能够使用增量 map/reduce。

如果您有几百千到几百万的食谱,您还可以添加一个 $out 阶段来预聚合您的数据。

关于测量

恕我直言,定义测量值毫无意义。有茶匙、汤匙、公制和英制测量值、分组如 "dozen" 或规格如 "clove"。您真的不想相互转换甚至设置为有限数量的测量值。一瓣大蒜有多少盎司? ;)

底线:将其设为自由文本字段,也许还有一些自动完成建议。

修改后的数据模型

食谱

{
  _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