刚刚使用MongoDB深入研究NoSQL的世界,我仍然在努力理解最佳的架构方法,而不是先对数据进行规范化,然后加入它。目前我正在设计的项目是一个简单的文章集,类似于维基。一篇文章将有一个标题和文本,以及(可能)一篇父文章和一篇或多篇儿童文章。
我对数据库设计有很多不同的想法,并希望选择最符合MongoDB强度的那个。
Idea One
由于数据库上最常见的查询类型总是简单地检索文章,因此我将嵌入页面所需的所有相关数据以显示所有内容。当然,实际的文章,以及带有 url 的父子文档(与其他文档的_id匹配)以及 title 这是一个文本我们将在屏幕上打印出标签内部。 孩子存在一个身份结构,但它是一个数组,以便所有孩子都存在。
{
"_id" : "test-article-2",
"title" : "Test Article 2",
"text" : "Blah 2",
"parent" : {
"title" : "Test Article",
"url" : "test-article"
},
"children" : [
{
"title" : "Test Article 3",
"url" : "test-article-3"
}
]
}
这种类型的设计似乎具有速度的优势(在我看来),但我想听听其他设计的内容。
理念二
更多的是我习惯来自关系数据库世界。不是将子对象嵌入到设计中,而是简单地放入它们的唯一标识符。因此,父现在只包含一个与其他文档的 _id 匹配的文本字符串,子同样会包含一个链接数组到 _id s。
为了获得查看文章的所有信息,我们现在需要进行一些查询(至少我认为我们需要...)一个获取主要文章,然后另一个获取父母的标题放入标签,然后是另一个获得所有儿童文章,同样获得他们的标题。
这似乎只是为了显示一篇文章而进行更多的查询,但是如果删除或更新了一些文章,它可能会使数据库的更新变得更容易。 (再次不确定那一点)。
{
"_id" : "test-article-2",
"title" : "Test Article 2",
"text" : "Blah 2",
"parent" : "test-article",
"children" : [ "test-article-3", "test-article-4"]
}
很高兴听到那些有更多MongoDB设计经验的人的意见。
答案 0 :(得分:19)
您需要考虑需要执行的查询类型以及每种类型需要的频率。当我在做类似的事情时,我提出了六个可能的行动:
您想要估算这些对您的应用程序的重要性。
如果您的大部分工作涉及使用某些特定文章(包括其直接的父母和子女)的存储数据,第一个想法最有用。实际上,在MongoDB中,将所需的所有信息放在同一文档中而不是在外部引用它是很常见的,这样您只需要检索一件事并只使用该数据。列表中的最后四个动作虽然更棘手。
特别是,在这种情况下,您将需要遍历树以检索祖先和后代,移动中间文档并遵循路径,即使您可能只关心路径中的最后一个文档。对于长层次结构,这可能很慢。改变关系可能需要在多个文档中移动大量信息,因为每个文档中存在所有数据。但即使改变像#34; title"可能很烦人,因为您必须考虑这个字段存在于多个不同文档中的事实,无论是作为主要字段还是在父字段或子字段下。
基本上,您的第一个想法在更多静态应用程序中效果最佳,在最初创建数据后,您不会在很多时候更改数据,但您需要在哪里定期阅读。
MongoDB文档具有five recommended approaches,用于处理树状(分层)结构。所有这些都有不同的优点和缺点,尽管它们只需要在一篇文档中更新主要数据就可以轻松更新。
MongoDB documentation中详细讨论了这五种方法。
第二个想法结合了"家长参考"和儿童参考"儿童参考"上面讨论的方法。这种方法可以很容易地找到子项和父项,并且可以轻松更新文章的关系和主要数据(尽管您需要更新父项和子项),但仍需要遍历它寻找祖先和后代。
如果您有兴趣寻找祖先和后代(并且关心这一点而不是能够轻松更新关系),您可以考虑在您的第二个想法中添加祖先数组,以便查询祖先和后代。当然,如果你这样做,更新关系会变得非常痛苦。
<强>结论:强>
最终,这一切都取决于最需要的行动。由于您正在处理文章,其基础数据(如标题)可能经常更改,您可能希望避免第一个想法,因为您不仅需要更新该文章的主文档,还需要更新所有子文档作为父母。
您的第二个想法可以轻松检索直接的父母和孩子。更新关系也不是太困难(它肯定比其他一些可用的选项更好)。
如果您真的希望以牺牲更新关系为代价来轻松找到祖先和后代,请选择包含一系列祖先引用。
通常,尽量减少所需的遍历次数,因为它们需要运行某种迭代或递归来获取所需的数据。如果您重视更新关系的能力,您还应该选择一个更改树中较少节点的选项(父引用,子引用,您的第二个想法可以执行此操作)。
答案 1 :(得分:1)
来自@CynicalProgrammer的非常好的总结,我还要补充一点:使用json是你的冒险树的事实!无需在每个节点中仅存储“左”和“右”节点ID,为什么不存储子树3-4-5深度?所以它看起来像这样:
{
left: {
left: {
left: {...},
right: {...}
},
right: {...}
},
right: {... you get the idea ...}
}
通过这种方式,您需要更少的查询来遍历树,并且集合中的文档数量将是一小部分。一个小缺点是你需要一些更复杂的代码来写入树,mongodb文件会更大,这意味着个别写入速度较慢。
我认为这可能是在mongodb中储存树木的最佳方式。但是请记住,mongo用于文档存储,而不是“大屁股无模式树”存储。您可能想要查看类似neo4j的内容。