我们有多个产品的嵌套类别(例如,体育 - >篮球 - >男士,体育 - >网球 - >女士)并正在使用Mongo而不是MySQL。
我们知道如何将嵌套类别存储在像MySQL这样的SQL数据库中,但是对于如何为Mongo做什么建议表示赞赏。我们需要优化的操作是快速查找一个类别或子类别中的所有产品,这些类别或子类别可以嵌套在根类别下面的几个层次(例如,男子篮球类别中的所有产品或者女子网球类别)。
This Mongo doc提出了一种方法,但它表示当我们需要子树需要操作时它不能很好地工作(因为类别可以达到多个级别)。
有关有效存储和搜索任意深度嵌套类别的最佳方法的任何建议?
答案 0 :(得分:11)
您要决定的第一件事就是您将使用哪种树。
要考虑的重要事项是您的数据和访问模式。您已经说过,所有工作的90%都将被查询,而且它的声音(电子商务)更新只会由管理员运行,很可能很少。
所以你想要一个能让你通过路径快速查询孩子的方案,即:体育 - >篮球 - >男士,运动 - >网球 - >女性,并不真正需要真正扩展到更新。
正如你正确指出的那样MongoDB确实有一个很好的文档页面:http://docs.mongodb.org/manual/tutorial/model-tree-structures/ 10gen实际上说明了树的不同模型和模式方法,并描述了它们的主要起伏。
如果您想要轻松查询,应该吸引眼球的是物化路径:https://docs.mongodb.com/manual/tutorial/model-tree-structures-with-materialized-paths/
这是一个非常有趣的构建树的方法,因为查询上面给出的例子" Womens"在"网球"你可以简单地做一个预先固定的正则表达式(可以使用索引:http://docs.mongodb.org/manual/reference/operator/regex/),如下所示:
db.products.find({category: /^Sports,Tennis,Womens[,]/})
查找树木特定路径下列出的所有产品。
不幸的是,这种模式在更新时非常糟糕,如果您移动类别或更改其名称,则必须更新所有产品,并且在一个类别下可能有数千种产品。
更好的方法是在产品上放置cat_id
,然后将类别分成带有架构的单独集合:
{
_id: ObjectId(),
name: 'Women\'s',
path: 'Sports,Tennis,Womens',
normed_name: 'all_special_chars_and_spaces_and_case_senstive_letters_taken_out_like_this'
}
所以现在你的查询只涉及类别集合,这应该使它们更小,更高效。例外情况是当您删除某个类别时,产品仍需要触摸。
这是一个改变网球"的例子。到了#34; Badmin":
db.categories.update({path:/^Sports,Tennis[,]/}).forEach(function(doc){
doc.path = doc.path.replace(/,Tennis/, ",Badmin");
db.categories.save(doc);
});
不幸的是,MongoDB目前没有提供查询文档反映,所以你必须将它们拉出客户端,这有点烦人,但希望它不会导致太多的类别被带回来。
这基本上就是它的工作原理。更新有点痛苦但是能够使用索引在任何路径上立即查询的能力更适合我相信的场景。
当然,额外的好处是这个架构与嵌套集模型兼容:http://en.wikipedia.org/wiki/Nested_set_model我一次又一次地发现它对于电子商务网站来说真是太棒了,例如,网球可能在两者之下#34;体育"和"休闲"并且您需要多个路径,具体取决于用户来自何处。
物化路径的架构通过添加另一个path
就可以轻松支持这一点。
希望它有意义,在那里相当长。
答案 1 :(得分:4)
如果所有类别都不同,那么将它们视为标签。层次结构不需要在项目中进行编码,因为在查询项目时不需要它们。层次结构是表达性的东西。使用其路径中的所有类别标记每个项目,因此“Sport> Baseball> Shoes”可以保存为{..., categories: ["sport", "baseball", "shoes"], ...}
。如果您想要“体育”类别中的所有项目,请搜索{categories: "sport"}
,如果您只想要鞋子,请搜索{tags: "shoes"}
。
这不会捕获层次结构,但如果您认为它无关紧要。如果类别不同,则在查询项目时,层次结构不会对您有所帮助。没有其他“棒球”,所以当你搜索它时,你只会得到层次结构中“棒球”级别以下的东西。
我的建议依赖于不同的类别,我猜他们不在你当前的模型中。但是,你没有理由不能让它们与众不同。您可能已选择使用您在页面上显示的字符串作为数据库中的类别名称。如果您改为使用“sport”或“womens_shoes”这样的符号名称,并使用查找表来查找要在页面上显示的字符串(如果类别的名称发生变化,这也将节省您的工作时间 - 它将会如果您需要这样做,可以更轻松地翻译网站)您可以轻松确保它们是截然不同的,因为它们与页面上显示的内容没有任何关系。因此,如果您在层次结构中有两个“鞋子”(例如“网球>女性>鞋子”和“网球>男士>鞋子”),您可以添加限定符以使其区别(例如“womens_shoes”)和“mens_shoes”,或“tennis_womens_shoes”)符号名称是任意的,可以是任何东西,你甚至可以使用数字,每次添加一个类别时只使用序列中的下一个数字。