18

いくつかの製品のカテゴリをネストしており(たとえば、スポーツ->バスケットボール->メンズスポーツ->テニス->ウィメンズ)、MySQLの代わりにMongoを使用しています。

ネストされたカテゴリをMySQLのようなSQLデータベースに保存する方法は知っていますが、Mongoで何をすべきかについてアドバイスをいただければ幸いです。最適化する必要のある操作は、ルートカテゴリの数層下にネストできる1つのカテゴリまたはサブカテゴリのすべての製品をすばやく見つけることです(たとえば、男子バスケットボールカテゴリのすべての製品または女子テニスカテゴリのすべての製品)。

このMongoドキュメントは、1つのアプローチを提案していますが、必要なサブツリーの操作が必要な場合はうまく機能しないと述べています(カテゴリは複数のレベルに達する可能性があるため)。

任意の深さのネストされたカテゴリを効率的に保存および検索するための最良の方法に関する提案はありますか?

4

2 に答える 2

16

The first thing you want to decide is exactly what kind of tree you will use.

The big thing to consider is your data and access patterns. You have already stated that 90% of all your work will be querying and by the sounds of it (e-commerce) updates will only be run by administrators, most likely rarely.

So you want a schema that gives you the power of querying quickly on child through a path, i.e.: Sports -> Basketball -> Men's, Sports -> Tennis -> Women's, and doesn't really need to truly scale to updates.

As you so rightly pointed out MongoDB does have a good documentation page for this: https://docs.mongodb.com/manual/applications/data-models-tree-structures/ whereby 10gen actually state different models and schema methods for trees and describes the main ups and downs of them.

The one that should catch the eye if you are looking to query easily is materialised paths: https://docs.mongodb.com/manual/tutorial/model-tree-structures-with-materialized-paths/

This is a very interesting method to build up trees since to query on the example you gave above into "Womens" in "Tennis" you could simply do a pre-fixed regex (which can use the index: http://docs.mongodb.org/manual/reference/operator/regex/ ) like so:

db.products.find({category: /^Sports,Tennis,Womens[,]/})

to find all products listed under a certain path of your tree.

Unfortunately this model is really bad at updating, if you move a category or change its name you have to update all products and there could be thousands of products under one category.

A better method would be to house a cat_id on the product and then separate the categories into a separate collection with the schema:

{
    _id: ObjectId(),
    name: 'Women\'s',
    path: 'Sports,Tennis,Womens',
    normed_name: 'all_special_chars_and_spaces_and_case_senstive_letters_taken_out_like_this'
}

So now your queries only involve the categories collection which should make them much smaller and more performant. The exception to this is when you delete a category, the products will still need touching.

So an example of changing "Tennis" to "Badmin":

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

Unfortunately MongoDB provides no in-query document reflection at the moment so you do have to pull them out client side which is a little annoying, however hopefully it shouldn't result in too many categories being brought back.

And this is basically how it works really. It is a bit of a pain to update but the power of being able to query instantly on any path using an index is more fitting for your scenario I believe.

Of course the added benefit is that this schema is compatible with nested set models: http://en.wikipedia.org/wiki/Nested_set_model which I have found time and time again are just awesome for e-commerce sites, for example, Tennis might be under both "Sports" and "Leisure" and you want multiple paths depending on where the user came from.

The schema for materialised paths easily supports this by just adding another path, that simple.

Hope it makes sense, quite a long one there.

于 2013-02-23T13:12:36.407 に答える
4

すべてのカテゴリが異なる場合は、それらをタグと考えてください。項目のクエリを実行するときに階層は必要ないため、階層を項目にエンコードする必要はありません。階層は表示上のものです。各項目にパス内のすべてのカテゴリをタグ付けして、「スポーツ > 野球 > 靴」を として保存できるようにし{..., categories: ["sport", "baseball", "shoes"], ...}ます。「スポーツ」カテゴリのすべてのアイテムが必要な場合は を検索し{categories: "sport"}、シューズだけが必要な場合は を検索し{tags: "shoes"}ます。

これは階層を捉えていませんが、考えてみれば問題ありません。カテゴリが異なる場合、項目を照会するときに階層は役に立ちません。他に「野球」はないので、検索すると「野球」以下の階層のものしか出てきません。

私の提案は、カテゴリが明確であることに依存しており、現在のモデルには含まれていないと思います。ただし、それらを区別できない理由はありません。おそらく、ページに表示する文字列をデータベースのカテゴリ名として使用することを選択したでしょう。「sport」や「womens_shoes」などの記号名を使用し、ルックアップ テーブルを使用してページに表示する文字列を検索すると (これにより、カテゴリの名前が変更された場合に作業時間を節約できます。必要に応じて、サイトの翻訳を簡単にします) ページに表示されるものとは何の関係もないため、それらが明確であることを簡単に確認できます。したがって、階層に 2 つの「靴」がある場合 (たとえば、「テニス > 女性 > 靴」と「テニス > 男性」)

于 2013-02-28T07:17:20.323 に答える