2013-02-19 4 views
9

Nous avons des catégories imbriquées pour plusieurs produits (par exemple, Sports -> Basketball -> Hommes, Sports -> Tennis -> Femmes) et utilisent Mongo au lieu de MySQL.Le moyen le plus efficace de stocker des catégories imbriquées (ou des données hiérarchiques) dans Mongo?

Nous savons comment stocker des catégories imbriquées dans une base de données SQL comme MySQL, mais apprécierions tout conseil sur ce qu'il faut faire pour Mongo. L'opération que nous devons optimiser trouvera rapidement tous les produits de une catégorie ou sous-catégorie qui pourraient être emboîtés dans plusieurs catégories (par exemple, tous les produits dans la catégorie Basketball ou tous les produits dans la catégorie Tennis Femme).

This Mongo docThis Mongo doc suggère une approche, mais elle dit qu'elle ne fonctionne pas bien lorsque des opérations sont nécessaires pour les sous-arbres, dont nous avons besoin (puisque les catégories peuvent atteindre plusieurs niveaux).

Des suggestions sur la meilleure façon de stocker et de rechercher efficacement des catégories imbriquées de profondeur arbitraire?

+1

Les chemins matérialisés sont robustes à l'interrogation tandis que plus lent à la mise à jour – Sammaye

+1

le lien mongodb docs répertorie cinq approches, pas une et je pense que la troisième semble parfaitement adaptée à votre cas d'utilisation. –

Répondre

10

La première chose que vous voulez décider est exactement quel type d'arbre vous allez utiliser.

La grande chose à considérer est vos données et vos modèles d'accès. Vous avez déjà indiqué que 90% de tous vos travaux seront interrogés et que les mises à jour (e-commerce) ne seront effectuées par les administrateurs que très rarement.

Donc vous voulez un schéma qui vous donne le pouvoir d'interroger rapidement sur l'enfant à travers un chemin, à savoir: Sports -> Basketball -> Hommes, Sports -> Tennis -> Femmes, et n'a pas vraiment besoin d'échelle aux mises à jour.

Comme vous l'avez si justement souligné, MongoDB possède une bonne page de documentation: http://docs.mongodb.org/manual/tutorial/model-tree-structures/ où 10gen énonce des modèles et des méthodes de schémas différents pour les arbres et en décrit les principaux hauts et bas.

Celui qui devrait attirer l'attention si vous êtes à la recherche d'interroger les chemins se matérialise facilement: http://docs.mongodb.org/manual/tutorial/model-tree-structures/#model-tree-structures-with-materialized-paths

C'est une méthode très intéressante pour construire des arbres depuis pour interroger l'exemple que vous avez donné ci-dessus dans « Femmes » dans « Tennis » vous pouvez simplement faire une regex pré-fixe (qui peut utiliser l'index: http://docs.mongodb.org/manual/reference/operator/regex/) comme ceci:

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

pour trouver tous les produits énumérés sous un certain chemin de votre arbre.

Malheureusement ce modèle est vraiment mauvais à mettre à jour, si vous déplacez une catégorie ou changez son nom vous devez mettre à jour tous les produits et il pourrait y avoir des milliers de produits dans une catégorie.

Une meilleure méthode serait de loger un cat_id sur le produit, puis séparer les catégories dans une collection séparée avec le schéma:

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

Alors maintenant vos requêtes ne concernent que la collection des catégories qui devrait leur faire beaucoup plus petit et plus performant. L'exception à ceci est quand vous supprimez une catégorie, les produits auront toujours besoin de toucher.

Ainsi, un exemple de changement « Tennis » à « Badmin »:

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

Malheureusement MongoDB fournit pas dans requête le document de réflexion au moment afin que vous ne devez les retirer côté client qui est un peu ennuyeux, mais j'espère que cela ne devrait pas entraîner le retour de trop de catégories.

Et c'est fondamentalement comment cela fonctionne vraiment. C'est un peu difficile à mettre à jour, mais le pouvoir d'interroger instantanément n'importe quel chemin à l'aide d'un index est plus adapté à votre scénario, je crois.

Bien sûr, l'avantage supplémentaire est que ce schéma est compatible avec les modèles de jeux imbriqués: http://en.wikipedia.org/wiki/Nested_set_model que j'ai trouvé à maintes reprises sont tout simplement génial pour les sites de commerce électronique, par exemple, Tennis pourrait être à la fois "Sports" et "Loisirs" et vous voulez plusieurs chemins en fonction de l'origine de l'utilisateur.

Le schéma pour les chemins matérialisés supporte facilement cela en ajoutant simplement un autre path, aussi simple que cela.

Espérons que cela a du sens, assez long.

+0

Merci! Que faire si nous voulons avoir besoin de stocker des méta-informations de catégorie (par exemple, nom et identifiant)? Devrions-nous mettre de côté une collection séparée pour les catégories, puis utiliser l'ID dans le chemin de catégorie pour les produits? Nous ne nous attendons pas à ce que les informations sur la catégorie changent très souvent, peut-être une fois par an. – Crashalot

+0

@Crashalot Oui si quelque chose est assigné à la catégorie, il est normalement préférable de le stocker dans la catégorie, l'alternative est de le stocker sur chaque produit et même si cela ne change pas souvent, il semble logique que lorsque vous obtenez la catégorie pour obtenir ses métadonnées ainsi que les produits – Sammaye

+0

Cool, merci pour la confirmation. Cela suggère également que nous stockons l'identifiant de la catégorie dans le chemin au lieu du nom de la catégorie. Y a-t-il quelque chose que vous voyez qui ne va pas avec ça? Avez-vous de l'expérience dans le stockage et l'interrogation de données hiérarchiques dans Mongo? Juste curieux si vous êtes intéressé par un petit projet de consultation. :) – Crashalot

4

Si toutes les catégories sont distinctes, considérez-les comme des étiquettes. La hiérarchie n'est pas nécessaire pour encoder dans les éléments car vous n'en avez pas besoin lorsque vous recherchez des éléments. La hiérarchie est une chose de présentation. Marquer chaque élément avec toutes les catégories dans son chemin, donc "Sport> Baseball> Chaussures" pourrait être enregistré comme {..., categories: ["sport", "baseball", "shoes"], ...}. Si vous voulez tous les articles dans la catégorie "Sport", recherchez {categories: "sport"}, si vous voulez seulement les chaussures, recherchez {tags: "shoes"}. Cela ne capture pas la hiérarchie, mais si vous y réfléchissez, cela n'a pas d'importance. Si les catégories sont distinctes, la hiérarchie ne vous aide pas lorsque vous recherchez des éléments. Il n'y aura pas d'autre "baseball", donc quand vous recherchez cela, vous n'obtiendrez que des choses en dessous du niveau "baseball" dans la hiérarchie. Ma suggestion repose sur les catégories étant distinctes, et je suppose qu'elles ne sont pas dans votre modèle actuel. Cependant, il n'y a aucune raison pour laquelle vous ne pouvez pas les distinguer. Vous avez probablement choisi d'utiliser les chaînes que vous affichez sur la page en tant que noms de catégorie dans la base de données. Si vous utilisez plutôt des noms symboliques comme "sport" ou "womens_shoes" et utilisez une table de recherche pour trouver la chaîne à afficher sur la page (cela vous épargnera également des heures de travail si le nom d'une catégorie change jamais - et il rendre la traduction du site plus facile, si vous avez besoin de le faire), vous pouvez facilement vous assurer qu'ils sont distincts car ils n'ont rien à voir avec ce qui est affiché sur la page. Donc, si vous avez deux "Chaussures" dans la hiérarchie (par exemple "Tennis> Femmes> Chaussures" et "Tennis> Homme> Chaussures") vous pouvez simplement ajouter un qualificatif pour les rendre distinctes (par exemple "womens_shoes" et "mens_shoes" , ou "tennis_womens_shoes") Les noms symboliques sont arbitraires et peuvent être n'importe quoi, vous pourriez même utiliser des nombres et juste utiliser le numéro suivant dans la séquence chaque fois que vous ajoutez une catégorie.

+0

La dernière partie de votre réponse en utilisant les qualificatifs de ce genre ressemble beaucoup à des chemins matérialisés, sauf qu'elle n'a pas de véritable standardisation de sa profondeur et de sa formation perçues, que certains pourraient considérer comme mauvaises à cet égard. – Sammaye

+1

Ce n'est certainement pas des chemins matérialisés, je ne suggère pas que les noms symboliques devraient inclure la hiérarchie complète, ils peuvent être complètement arbitraires. Mes exemples incluent seulement des parties de la hiérarchie parce que les étiquettes étaient si génériques. Ils devraient être aussi précis que possible, mais pas plus. Je crois que l'encodage de la hiérarchie dans les éléments de la base de données est un anti-pattern. La hiérarchie est un détail de présentation, et l'utilisation de chemins matérialisés répète inutilement la hiérarchie de chaque élément, rendant le modèle de données fragile et rendant inutilement difficile la modification ultérieure de la hiérarchie. – Theo

Questions connexes