在Mongo中存储嵌套类别(或分层数据)的最有效方法?

Cra*_*lot 11 database mongodb nosql

我们有几个产品的嵌套类别(例如,体育 - >篮球 - >男子,体育 - >网球 - >女子),并使用Mongo而不是MySQL.

我们知道如何将嵌套类别存储在像MySQL这样的SQL数据库中,但是对于如何为Mongo做什么建议表示赞赏.我们需要优化的操作是快速查找一个类别或子类别中的所有产品,这些类别或子类别可以嵌套在根类别下面的几个层(例如,男士篮球类别中的所有产品或女子网球类别中的所有产品).

这个Mongo文档提出了一种方法,但它表示当我们需要子树需要操作时它不能很好地工作(因为类别可以达到多个级别).

有关有效存储和搜索任意深度的嵌套类别的最佳方法的任何建议?

Sam*_*aye 11

您要决定的第一件事就是您将使用哪种树.

需要考虑的重要事项是您的数据和访问模式.您已经说过,您所有工作的90%都将被查询,并且它的声音(电子商务)更新将仅由管理员运行,很可能很少.

所以你想要一个模式,让你通过一条路径快速查询孩子,即:体育 - >篮球 - >男子,体育 - >网球 - >女子,并不真正需要真正扩展到更新.

正如你正确指出的那样MongoDB确实有一个很好的文档页面:http://docs.mongodb.org/manual/tutorial/model-tree-structures/其中10gen实际上说明了树的不同模型和模式方法,并描述了他们的主要起伏.

如果您希望轻松查询,应该引起注意的是物化路径:http://docs.mongodb.org/manual/tutorial/model-tree-structures/#model-tree-structures-with-materialized-paths

这是一个非常有趣的构建树的方法,因为查询上面给出的"网球"中的"女性"的例子,你可以简单地做一个预先固定的正则表达式(可以使用索引:http://docs.mongodb .org/manual/reference/operator/regex /)像这样:

db.products.find({category: /^Sports,Tennis,Womens[,]/})
Run Code Online (Sandbox Code Playgroud)

查找树的特定路径下列出的所有产品.

不幸的是,这种模式在更新时非常糟糕,如果您移动类别或更改其名称,您必须更新所有产品,并且在一个类别下可能有数千种产品.

一个更好的方法是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'
}
Run Code Online (Sandbox Code Playgroud)

所以现在你的查询只涉及类别集合,这应该使它们更小,更高效.例外情况是当您删除类别时,产品仍需要触摸.

这是将"网球"改为"Badmin"的一个例子:

db.categories.update({path:/^Sports,Tennis[,]/}).forEach(function(doc){
    doc.path = doc.path.replace(/,Tennis/, ",Badmin");
    db.categories.save(doc);
});
Run Code Online (Sandbox Code Playgroud)

不幸的是,MongoDB目前不提供查询文档反射,所以你必须将它们拉出客户端,这有点烦人,但希望它不会导致太多的类别被带回来.

这基本上就是它的工作原理.更新有点痛苦,但是能够使用索引在任何路径上即时查询的能力更适合我的情况.

当然,额外的好处是这个模式与嵌套集模型兼容:http://en.wikipedia.org/wiki/Nested_set_model我一次又一次地发现它对电子商务网站来说很棒,例如,网球可能在"体育"和"休闲"之下,你需要多个路径,具体取决于用户来自哪里.

物化路径的模式通过添加另一个path简单的方式轻松支持这一点.

希望它有意义,在那里相当长.