2011-10-03 11 views
5

Questa è una continuazione del progetto indicato nella domanda this.Catalogo prodotti query Catalogo RavenDB per aggregazione di specifiche su raccolta arbitraria di prodotti

ho il seguente modello:

class Product { 
    public string Id { get; set; } 
    public string[] Specs { get; set; } 
    public int CategoryId { get; set; } 
} 

Le "caratteristiche" negozi matrice Theproductspecification coppie valore nome uniti da un carattere speciale. Ad esempio, se un prodotto è colorato in blu, la stringa della specifica sarà "Colore ~ ​​Blu". La rappresentazione delle specifiche in questo modo consente di eseguire query per prodotti con più valori di specifica specificati da una query. Esistono due query principali che desidero supportare:

  1. Ottenere tutti i prodotti in una determinata categoria.
  2. Ottieni tutti i prodotti in una determinata categoria che hanno una serie di specifiche specificate.

Questo funziona bene con RavenDB. Tuttavia, oltre ai prodotti che soddisfano una determinata query, desidero restituire un set di risultati che contenga tutte le coppie nome-valore per l'insieme di prodotti specificati dalla query. Le coppie nome-valore della specifica devono essere raggruppate in base al nome e al valore della specifica e contenere un conteggio di prodotti che hanno una specifica coppia nome-valore. Per la query # 1 ho creato la seguente mappa ridurre indice:

class CategorySpecGroups { 
    public int CategoryId { get; set; } 
    public string Spec { get; set; } 
    public int Count { get; set; } 
} 


public class SpecGroups_ByCategoryId : AbstractIndexCreationTask<Product, CategorySpecGroups> 
{ 
    public SpecGroups_ByCategoryId() 
    { 
     this.Map = products => from product in products 
           where product.Specs != null 
           from spec in product.Specs 
           select new 
           { 
            CategoryId = product.CategoryId, 
            Spec = spec, 
            Count = 1 
           }; 

     this.Reduce = results => from result in results 
           group result by new { result.CategoryId, result.Spec } into g 
           select new 
           { 
            CategoryId = g.Key.CategoryId, 
            Spec = g.Key.Spec, 
            Count = g.Sum(x => x.Count) 
           }; 
    } 
} 

Posso quindi interrogare questo indice e ottenere tutte le coppie nome-valore spec in una determinata categoria. Il problema che sto incontrando è quello di ottenere lo stesso set di risultati ma per una query che filtra sia da una categoria che da un insieme di coppie nome-valore della specifica. Quando si utilizza SQL questo set di risultati si otterrebbe facendo un gruppo su un insieme di prodotti filtrati per categoria e specifiche. In generale, questo tipo di query è costoso, ma quando si filtrano sia per categoria che per specifiche, i set di prodotti sono normalmente piccoli, sebbene non abbastanza piccoli da adattarsi a una singola pagina: possono contenere fino a 1000 prodotti. Per riferimento, MongoDB supporta un metodo group che può essere utilizzato per ottenere lo stesso set di risultati. Questo esegue il lato server di raggruppamento ad hoc e le prestazioni sono accettabili.

Come posso ottenere questo tipo di set di risultati utilizzando RavenDB?

Una possibile soluzione è ottenere tutti i prodotti per una query ed eseguire il raggruppamento in memoria e un'altra opzione è creare un indice mapreduce come sopra, sebbene la sfida con questo sarebbe la deduzione di tutte le possibili selezioni di specifiche che possono essere fatte per una determinata categoria e inoltre, questo tipo di indice potrebbe esplodere in termini di dimensioni.

Per un esempio, dare un'occhiata a this fastener category page. L'utente può filtrare la propria selezione selezionando gli attributi. Quando viene selezionato un attributo, restringe la selezione dei prodotti e visualizza gli attributi all'interno del nuovo set di prodotti. Questo tipo di interazione viene in genere chiamato faceted search.

EDIT

Nel frattempo, sarò tentando una soluzione che utilizza Solr come sostengono ricerca sfaccettata, fuori dalla scatola.

EDIT 2

sembra che RavenDB supporta anche faceted search (che ovviamente ha un senso, gli indici vengono memorizzati dal Lucene proprio come Solr). Esplorerò questo e pubblicherò aggiornamenti.

EDIT 3

La funzionalità di ricerca RavenDB sfaccettato funziona come previsto. Conservo un documento di impostazione della faccetta per ogni ID di categoria che viene utilizzato per calcolare le faccette per una query all'interno di una determinata categoria. Il problema che sto avendo ora è la performance. Per una raccolta di prodotti 500k con 4500 categorie distinte che risultano in 4500 documenti di configurazione dei facet, una query per ID categoria impiega circa 16 secondi quando esegue anche query per facet e circa 0,05 secondi quando non esegue query per le faccette. La particolare categoria testata contiene circa 6k prodotti, 23 sfaccettature distinte e 2k distinte combinazioni di nomi. Dopo aver esaminato il codice in FacetedQueryRunner, viene visualizzata una query facet che genererà una query Lucene per ogni combinazione nome-valore di facet per ottenere i conteggi, oltre a una query per ogni nome di facet per ottenere i termini. Un problema con l'implementazione è che recupererà tutti i termini distinti per un determinato nome di facet indipendentemente dalla query, che nella maggior parte dei casi ridurrà significativamente il numero di termini per un facet e quindi ridurrà il numero di query Lucene. Un modo per migliorare le prestazioni qui sarebbe quello di memorizzare un set di risultati calcolati MapReduce (come mostrato sopra) per ciascun documento di impostazione di sfaccettatura, che potrebbe quindi essere interrogato per ottenere tutti i termini distinti quando vengono ulteriormente filtrati per sfaccettature. Tuttavia, le prestazioni generali potrebbero essere ancora troppo lente.

+0

Non credo di aver capito. Qual è la query che vuoi fare? Qual è il risultato previsto? –

+0

Prendiamo ad esempio il set di prodotti definiti da un ID di categoria - tutti i prodotti in una determinata categoria. Ogni prodotto nel set ha un insieme di coppie nome-valore specifiche che possono essere raggruppate per nome e valore contando il numero di prodotti con una data coppia di valori nominali. Questo è il risultato che vorrei ottenere, ma non solo per i prodotti in una determinata categoria, ma anche per le categorie in una categoria filtrata da un insieme di coppie nome-valore specifiche. Questo per consentire di "forare" una selezione di prodotti su un catalogo di prodotti di un sito Web. – eulerfx

risposta

3

Ho implementato questa funzione utilizzando RavenDB faceted search, tuttavia ho apportato alcune modifiche a FacetedQueryRunner per supportare un'ottimizzazione euristica. L'euristica è che, nel mio caso, le sfaccettature vengono visualizzate solo nelle categorie foglia. Questo è un limite ragionevole poiché la navigazione tra le categorie radice e interna può essere guidata da ricerche o elenchi di categorie secondarie.

Ora, dato il vincolo, memorizzo un documento FacetSetup per ogni categoria di fogli con l'ID che è qualcosa come "facets/category_123". Quando il documento di impostazione della faccetta viene memorizzato, ho accesso ai nomi delle faccette e ai valori dei facet (o intervalli) contenuti nella categoria. Pertanto, è possibile memorizzare tutti i valori di sfaccettatura disponibili nella raccolta Intervalli di ogni sfaccettatura nel documento FacetSetup, tuttavia la modalità di sfaccettatura è ancora FacetMode.Default.

Here are le modifiche a FacetedQueryRunner. Nello specifico, l'ottimizzazione verifica se un determinato aspetto memorizza intervalli, nel qual caso restituisce quei valori da utilizzare per la ricerca anziché ottenere tutti i termini in un indice associato a un dato aspetto. Nella maggior parte dei casi questo ridurrà in modo significativo il numero di ricerche Lucene richieste poiché i valori di facet disponibili in una data categoria sono un sottoinsieme di valori di facet nell'intero indice.

La prossima ottimizzazione che è possibile fare è che se la query originale filtra solo per un id di categoria, allora il documento FacetSetup può effettivamente memorizzare anche i conteggi. Un modo, anche se hacky, per farlo sarebbe quello di aggiungere il conteggio a ciascun valore di facet nella raccolta Ranges, quindi aggiungere un booleano al documento FacetSetup per indicare che i conteggi vengono aggiunti. Ora questa query di faccette restituirà fondamentalmente i valori nel documento FacetSetup: non è necessario eseguire una query.

Una considerazione ora sarebbe quello di mantenere i documenti FacetSetup fino ad oggi, tuttavia questo sarebbe richiesto in entrambi i modi. Oltre a questo si può utilizzare il caching dell'ottimizzazione, che credo sia l'approccio adottato dalla ricerca sfaccettata Solr.

Inoltre, sarebbe bello se i documenti FacetSetup dove sincronizzato automaticamente con la raccolta del prodotto in quanto effettivamente essi sono il risultato di un'operazione MapReduce aggregando il set di prodotti raggruppamento inizialmente per categoria id, quindi il nome della faccetta e poi i valori.

Problemi correlati