我如何使用 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: ....
}
}
}, { ... }]
但不是得到
[]
恕我直言,你的设计完全错误。您过度规范化了数据。我会做一些更简单的事情并使用嵌入。其背后的原因是,您先定义用例,然后对数据建模,以最有效的方式回答用例产生的问题。
假设的用例
- 作为用户,我想要所有食谱的列表。
- 作为用户,我想要一份按成分列出的所有食谱的列表。
- 作为一名设计师,我希望能够展示所有成分的清单。
- 作为用户,我希望能够 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
最近我使用 Hackalode 设计了一个数据库模型或 ERD。 所以我目前面临的问题是,基于我目前的设计,我无法按照我的意愿正确查询它。我用 MYSQL 研究了 ERD,并且确实知道 Mongo 的工作原理不同
这个想法很简单,我想要一个包含一系列成分列表的食谱,并且这些成分来自单独的集合。 食谱还包括成分的测量,即。 (1 汤匙糖)
也可以从配料列表中查询并找到包含该配料的食谱
我希望这个集合是多对多的关系,并且食谱可以使用数据库中已有的成分。
就是不知道怎么查询数据
我通过使用 $elemMatch 和 populate 尝试了很多方法,结果我得到的只是空数组列表。
我期待两种类型的查询,我可以通过成分名称或配方进行查询
我的预期结果是这样的
[{
id: ...,
name: ....,
description: ...,
macros: [...],
ingredients: [
{
id,
amount: ....,
unit: ....
ingredient: {
id: ....,
name: ....
}
}
}, { ... }]
但不是得到
[]
恕我直言,你的设计完全错误。您过度规范化了数据。我会做一些更简单的事情并使用嵌入。其背后的原因是,您先定义用例,然后对数据建模,以最有效的方式回答用例产生的问题。
假设的用例
- 作为用户,我想要所有食谱的列表。
- 作为用户,我想要一份按成分列出的所有食谱的列表。
- 作为一名设计师,我希望能够展示所有成分的清单。
- 作为用户,我希望能够 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