2013-02-19 17 views
9

Abbiamo categorie nidificate per diversi prodotti (ad es., Sport -> Basket -> Uomini, Sport -> Tennis -> Donne e stiamo usando Mongo anziché MySQL.Il modo più efficiente di memorizzare categorie nidificate (o dati gerarchici) in Mongo?

Sappiamo come memorizzare le categorie nidificate in un database SQL come MySQL, ma apprezzeremmo qualsiasi consiglio su cosa fare per Mongo. L'operazione che dobbiamo ottimizzare per trovare rapidamente tutti i prodotti in una categoria o sottocategoria, che potrebbe essere annidata diversi livelli sotto una categoria radice (ad esempio, tutti i prodotti nella categoria Menball Basket o tutti i prodotti nella categoria Tennis femminile).

This Mongo doc suggerisce un approccio, ma dice che non funziona bene quando sono necessarie operazioni per sottoalberi, di cui abbiamo bisogno (poiché le categorie possono raggiungere più livelli).

Qualche suggerimento sul modo migliore per archiviare e cercare in modo efficiente categorie annidate di profondità arbitrarie?

+1

I percorsi materializzati sono efficaci durante l'interrogazione mentre l'aggiornamento è più lento – Sammaye

+1

il collegamento mongodb docs elenca cinque approcci, non uno e penso che il terzo aspetto sia perfettamente adeguato per il tuo caso d'uso. –

risposta

10

La prima cosa che vuoi decidere è esattamente quale tipo di albero utilizzerai.

La cosa importante da considerare sono i dati e i modelli di accesso. Hai già affermato che il 90% di tutto il tuo lavoro verrà sottoposto a query e, con il suo suono (e-commerce), gli aggiornamenti verranno eseguiti solo dagli amministratori, molto probabilmente raramente.

Quindi vuoi uno schema che ti dia la possibilità di interrogare rapidamente sul bambino attraverso un percorso, ad esempio: Sport -> Basket -> Uomo, Sport -> Tennis -> Donna, e non ha davvero bisogno di scalare veramente agli aggiornamenti.

Come hai giustamente osservato MongoDB ha una buona pagina di documentazione per questo: http://docs.mongodb.org/manual/tutorial/model-tree-structures/ in cui 10gen in realtà indica modelli diversi e metodi di schema per alberi e descrive i principali alti e bassi di essi.

Quello che dovrebbe catturare l'attenzione se si sta cercando di interrogare i percorsi facilmente si materializza: http://docs.mongodb.org/manual/tutorial/model-tree-structures/#model-tree-structures-with-materialized-paths

questo è un metodo molto interessante per costruire alberi in quanto per interrogare l'esempio che hai dato sopra in "Donna" in "Tennis" si potrebbe semplicemente fare una regex prefissato (che può utilizzare l'indice: http://docs.mongodb.org/manual/reference/operator/regex/) in questo modo:

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

per trovare tutti i prodotti elencati sotto un certo percorso del vostro albero.

Purtroppo questo modello è davvero pessimo in fase di aggiornamento, se si sposta una categoria o si modifica il suo nome è necessario aggiornare tutti i prodotti e potrebbero esserci migliaia di prodotti in una categoria.

Un metodo migliore sarebbe quello di ospitare una cat_id sul prodotto e quindi separare le categorie in una raccolta differenziata con lo 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' 
} 

Così ora le vostre domande riguardano solo la raccolta categorie che dovrebbe renderli molto più piccolo e più performante. L'eccezione a questo è quando si elimina una categoria, i prodotti dovranno ancora toccare.

Così un esempio di modifica "Tennis" a "Badmin":

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

Purtroppo MongoDB fornisce alcuna riflessione documento-query al momento quindi che c'è bisogno di tirare fuori lato client che è un po ' fastidioso, tuttavia, si spera, non dovrebbe comportare il rientro di troppe categorie.

E questo è fondamentalmente come funziona davvero. È un po 'difficile aggiornare, ma il potere di essere in grado di eseguire query istantaneamente su qualsiasi percorso utilizzando un indice è più adatto per il tuo scenario.

Ovviamente il vantaggio è che questo schema è compatibile con i modelli di serie annidati: http://en.wikipedia.org/wiki/Nested_set_model che ho trovato più e più volte fantastico per i siti di e-commerce, ad esempio, Tennis potrebbe essere sotto entrambi gli "Sport" e "Tempo libero" e desideri percorsi multipli a seconda della provenienza dell'utente.

Lo schema per percorsi materializzati supporta facilmente questo semplicemente aggiungendo un altro path, che semplice.

Spero che abbia senso, piuttosto lungo.

+0

Grazie! Cosa succederebbe se avessimo bisogno di memorizzare le meta-informazioni di categoria (ad es. Nome e id)? Dovremmo mettere da parte una collezione separata per categorie quindi utilizzare l'ID nel percorso della categoria per i prodotti? Non ci aspettiamo che le informazioni sulla categoria cambino molto spesso, forse una volta all'anno. – Crashalot

+0

@Crashalot Sì se qualcosa è assegnato alla categoria è normalmente meglio memorizzarlo nella categoria, l'alternativa è di memorizzarlo su ogni prodotto e anche se non cambierà spesso sembra logico che quando si ottiene la categoria si vorrà per ottenere i suoi metadati oltre ai prodotti – Sammaye

+0

Cool, grazie per la conferma. Ciò suggerisce anche che memorizziamo l'ID della categoria nel percorso anziché il nome della categoria. C'è qualcosa che vedi che non va in questo? Hai esperienza di archiviazione e interrogazione di dati gerarchici in Mongo? Sei curioso di sapere se sei interessato a un piccolo progetto di consulenza. :) – Crashalot

4

Se tutte le categorie sono distinte, pensatele come tag. La gerarchia non è necessaria per codificare gli elementi perché non sono necessari quando si esegue una query per gli elementi. La gerarchia è una cosa di presentazione. Tagga ogni oggetto con tutte le categorie nel suo percorso, quindi "Sport> Baseball> Scarpe" potrebbe essere salvato come {..., categories: ["sport", "baseball", "shoes"], ...}. Se si desidera tutti gli articoli nella categoria "Sport", cercare {categories: "sport"}, se si desidera solo le scarpe, cercare {tags: "shoes"}.

Questo non cattura la gerarchia, ma se ci pensi non importa. Se le categorie sono distinte, la gerarchia non ti aiuta quando esegui una query per gli elementi. Non ci sarà nessun "baseball", quindi quando lo cercherete otterrete solo le cose sotto il livello "baseball" nella gerarchia.

Il mio suggerimento si basa su categorie distinte, e suppongo che non siano nel modello attuale. Tuttavia, non c'è motivo per cui non puoi renderli distinti. Probabilmente hai scelto di utilizzare le stringhe visualizzate nella pagina come nomi di categorie nel database. Se invece usi nomi simbolici come "sport" o "womens_shoes" e usi una tabella di ricerca per trovare la stringa da mostrare sulla pagina (questo ti farà risparmiare ore di lavoro se il nome di una categoria dovesse mai cambiare - e lo farà rendere più facile la traduzione del sito, se mai avessi bisogno di farlo) puoi facilmente assicurarti che siano distinti perché non hanno nulla a che fare con ciò che viene visualizzato sulla pagina. Quindi se hai due "Scarpe" nella gerarchia (ad esempio "Tennis> Donne> Scarpe" e "Tennis> Uomini> Scarpe") puoi semplicemente aggiungere un qualificatore per renderli distinti (ad esempio "womens_shoes" e "mens_shoes" , o "tennis_womens_shoes") I nomi simbolici sono arbitrari e possono essere qualsiasi cosa, potresti anche usare i numeri e usare il numero successivo nella sequenza ogni volta che aggiungi una categoria.

+0

L'ultima parte della tua risposta che usa le qualificazioni del genere è molto simile a percorsi materializzati, tranne che non ha una vera standardizzazione alla sua profondità e formazione percepita, che alcuni potrebbero vedere come cattiva sotto questo aspetto. – Sammaye

+1

Non sono sicuramente percorsi materializzati, non suggerisco che i nomi simbolici debbano includere la gerarchia completa, possono essere completamente arbitrari. I miei esempi includono solo parti della gerarchia perché le etichette erano molto generiche. Dovrebbero essere il più specifici possibile, ma non di più. Credo che codificare la gerarchia negli elementi nel database sia un anti-pattern. La gerarchia è un dettaglio di presentazione e l'uso di percorsi materializzati ripete inutilmente la gerarchia per ogni elemento, rendendo fragile il modello di dati e rendendo inutilmente difficile modificare la gerarchia in un secondo momento. – Theo

Problemi correlati