Domanda

Come la progettazione di un database per supportare le seguenti funzionalità di tagging:

  • gli elementi possono avere un gran numero di tag
  • per la ricerca di tutti gli elementi che sono contrassegnati con un dato insieme di tag deve essere veloce (gli articoli devono avere TUTTI i tag, quindi è un E-ricerca, non di una O di ricerca)
  • la creazione e la scrittura di articoli può essere più lenta per consentire la ricerca rapida/lettura

Idealmente, la ricerca di tutti gli elementi che sono contrassegnati con (almeno) un insieme di n dati tag dovrebbe essere fatto utilizzando una singola istruzione SQL.Poiché il numero di tag per la ricerca e il numero di etichette su qualsiasi elemento sconosciuto e può essere alta, utilizzando Join è impraticabile.

Tutte le idee?


Grazie per tutte le risposte finora.

Se non sbaglio, però, il dato le risposte mostrano come fare un O-ricerca sul tag.(Selezionare tutti gli elementi che hanno uno o più di n tag).Sto cercando un efficiente E di ricerca.(Selezionare tutti gli elementi che hanno TUTTE le n tag - e forse di più.)

È stato utile?

Soluzione

Su ANDing:Suona come si sta cercando la "relazionale divisione" operazione. Questo articolo copre relazionale divisione in concisa e ancora comprendere modo.

A proposito di prestazioni:Una bitmap approccio basato su intuitivo suona come esso si adatta bene la situazione.Tuttavia, io non sono convinto che sia una buona idea per implementare la bitmap di indicizzazione "manualmente", come digiguru suggerisce:Sembra una situazione complicata, ogni volta che un nuovo tag vengono aggiunti(?) Ma alcuni DBMSes (tra cui Oracle) offerta indici bitmap che possono essere in qualche modo d'uso, a causa di un built-in sistema di indicizzazione non è la potenziale complessità di manutenzione indice;inoltre, un DBMS offre indici bitmap deve essere in grado di prendere in considerazione di loro in un modo corretto per compiere il piano di query.

Altri suggerimenti

Ecco un buon articolo su come usare i tag di schemi di Database:

http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/

lungo con performance test:

http://howto.philippkeller.com/2005/06/19/Tagsystems-performance-tests/

Nota che le conclusioni non sono molto specifiche di MySQL, che (almeno nel 2005 al tempo in cui fu scritto) era molto scarsa indicizzazione di testo completo caratteristiche.

Non vedo un problema con una soluzione semplice e lineare:Tabella degli elementi, tavolo per tag, crosstable per "etichettare"

Indici sulla tavola a croce dovrebbe essere abbastanza ottimizzazione.La selezione di appropriati elementi

SELECT * FROM items WHERE id IN  
    (SELECT DISTINCT item_id FROM item_tag WHERE  
    tag_id = tag1 OR tag_id = tag2 OR ...)  

E tagging sarebbe

SELECT * FROM items WHERE  
    EXISTS (SELECT 1 FROM item_tag WHERE id = item_id AND tag_id = tag1)  
    AND EXISTS (SELECT 1 FROM item_tag WHERE id = item_id AND tag_id = tag2)  
    AND ...

che non è, certo, non è così efficace per il gran numero di confrontare i tag.Se devi mantenere tag contare nella memoria, si potrebbe fare query per iniziare con i tag che non sono spesso così E la sequenza sarebbe valutato più veloce.A seconda del numero previsto di tag per essere confrontati e la speranza di abbinare ogni singolo di loro questo potrebbe essere OK soluzione, se siete alla partita di 20 etichette, e si aspettano che qualche elemento casuale, la corrispondenza di 15 di loro, allora questo sarebbe ancora essere pesante su un database.

Volevo solo evidenziare che l'articolo di @Jeff Atwood collegamenti (http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/) è molto approfondita (e discute i meriti di 3 schemi diversi approcci) e ha una buona soluzione per la query E, di solito, una performance migliore rispetto a quello che è stato detto qui, così lontano (cioènon utilizzare una subquery correlata per ogni termine).Anche un sacco di roba buona nei commenti.

ps - L'approccio che ognuno sta parlando qui è indicato come il "Toxi" soluzione in questo articolo.

Si potrebbe desiderare di sperimentare con un non-strettamente-soluzione di database come un Java Content Repository attuazione (ad esempio, Apache Jackrabbit) e utilizzare un motore di ricerca costruito sulla cima di che, come Apache Lucene.

Questa soluzione con la appropriati meccanismi di caching sarebbe forse una resa migliore di home-grown soluzione.

Tuttavia, non penso che in una piccola o media applicazione, avrete bisogno di un più sofisticato implementazione di database normalizzato accennato nei post precedenti.

EDIT:con il tuo chiarimento mi sembra più convincente per l'utilizzo di un JCR-come soluzione di con un motore di ricerca.Che permetterebbe di semplificare i programmi a lungo termine.

Il metodo più semplice è quello di creare un tag tabella.
Target_Type -- nel caso in cui si sono tagging più tabelle
Target - Per il record di essere taggati
Tag - Il testo del tag

Esecuzione di query sui dati sarebbe qualcosa di simile:

Select distinct target from tags   
where tag in ([your list of tags to search for here])  
and target_type = [the table you're searching]

AGGIORNAMENTO
Sulla base delle vostre esigenze E le condizioni, la query di cui sopra sarebbe trasformato in qualcosa di simile a questo

select target
from (
  select target, count(*) cnt 
  from tags   
  where tag in ([your list of tags to search for here])
    and target_type = [the table you're searching]
)
where cnt = [number of tags being searched]

Mi piacerebbe seconda @Zizzencs suggerimento che si potrebbe desiderare qualcosa che non è totalmente (R)DB-centric

In qualche modo, credo che l'utilizzo di pianura di tipo nvarchar campi archivio tag con una cache/indicizzazione potrebbe produrre risultati più velocemente.Ma questo è solo me.

Ho implementato sistemi di tagging utilizzando 3 tabelle per rappresentare una relazione Molti-a-Molti rapporti prima (Tag di Elemento ItemTags), ma suppongo che avrete a che fare con i tag in un sacco di posti, posso dirvi che con 3 tabelle di dover essere manipolato/interrogare simultaneamente tutto il tempo che sarà sicuramente rendere il codice più complesso.

Si potrebbe desiderare di prendere in considerazione se la complessità è valsa la pena.

Non sarà in grado di evitare di join e di essere ancora un po ' normalizzata.

Il mio approccio è quello di avere un Tag di Tabella.

 TagId (PK)| TagName (Indexed)

Quindi, avete un TagXREFID colonna nella tabella articoli.

Questo TagXREFID colonna è un FK ad un 3 ° tabella, io la chiamerò TagXREF:

 TagXrefID | ItemID | TagId

Quindi, per ottenere tutti i tag di un elemento potrebbe essere qualcosa di simile:

SELECT Tags.TagId,Tags.TagName 
     FROM Tags,TagXref 
     WHERE TagXref.TagId = Tags.TagId 
         AND TagXref.ItemID = @ItemID

E per ottenere tutti gli elementi per un tag, mi piacerebbe usare qualcosa di simile a questo:

SELECT * FROM Items, TagXref
     WHERE TagXref.TagId IN 
          ( SELECT Tags.TagId FROM Tags
                WHERE Tags.TagName = @TagName; )
     AND Items.ItemId = TagXref.ItemId;

E un sacco di tag insieme, Si sarebbe modificare la dichiarazione di cui sopra leggermente per aggiungere E Tag.TagName = @TagName1 E Tag.TagName = @TagName2 ecc...e creare dinamicamente la query.

Quello che mi piace fare è di avere un numero di tabelle che rappresentano i dati grezzi, quindi in questo caso avresti

Items (ID pk, Name, <properties>)
Tags (ID pk, Name)
TagItems (TagID fk, ItemID fk)

Questo funziona veloce per i tempi di scrittura, e mantiene tutto normalizzato, ma si può anche notare che, per ogni tag, avrete bisogno di partecipare a tavoli di due volte per ogni tag che si desidera, E quindi c'è anche lento a leggere.

Una soluzione per migliorare la lettura è quello di creare una cache di tabella il comando per l'impostazione di una stored procedure che, in sostanza, crea nuova tabella che rappresenta i dati in un formato appiattito...

CachedTagItems(ID, Name, <properties>, tag1, tag2, ... tagN)

Quindi si può considerare come spesso Articolo Etichettato tabella deve essere mantenuto aggiornato, se è su tutte le operazioni di inserimento, quindi chiamare la stored procedure in un cursore di inserimento evento.Se si tratta di un'attività oraria, quindi impostare un orario di lavoro per l'esecuzione.

Ora, per essere davvero abile nel recupero dei dati, è opportuno creare una stored procedure per ottenere i dati dal tag.Piuttosto che usare le query nidificate in un massiccio caso di dichiarazione, si desidera passare a un singolo parametro che contiene un elenco di tag che si desidera selezionare dal database e restituire un set di record di Elementi.Questo potrebbe essere meglio in formato binario, utilizzando gli operatori logici.

In formato binario, è facile spiegare.Diciamo che ci sono quattro etichette per essere assegnato a un elemento, in codice binario, possiamo rappresentare che

0000

Se tutti e quattro i tag assegnati a un oggetto, l'oggetto sarebbe simile a questa...

1111

Se solo i primi due...

1100

Allora è solo un caso di ricerca di valori binari con i numeri 1 e zero nella colonna che si desidera.Utilizzo di SQL Server, gli operatori bit a Bit, si può verificare che non vi è un 1 nella prima delle colonne utilizzando molto semplice query.

Controllare questo link per saperne di più.

Per parafrasare ciò che hanno detto gli altri:il trucco non è in schema, è in query.

L'ingenuo schema di Enti/Etichette/Etichette è la strada giusta da percorrere.Ma, come abbiamo visto, non è immediatamente chiaro come eseguire una query E con un sacco di tag.

Il modo migliore per ottimizzare la query sarà dipendente dalla piattaforma, quindi vi consiglio di ri-etichettatura tua domanda con il tuo database di ripristino e di cambiare il titolo in qualcosa di simile a "modo Ottimale per eseguire query E sulla codifica del database".

Ho un paio di suggerimenti per MS SQL, ma di astenersi in caso che non è la piattaforma che si sta utilizzando.

Una variazione al di sopra di risposta è di prendere i tag id, ordinamento, si combinano come un ^ stringa separata e hash di loro.Quindi semplicemente associare l'hash con la voce.Ogni combinazione di tag, produce una nuova chiave.Per fare una ricerca E semplicemente ri-creare l'hash con il tag id e di ricerca.La modifica dei tag, un elemento sarà causare l'hash essere ricreato.Gli elementi con lo stesso set di tag di condividere lo stesso hash della chiave.

Se hai un tipo di matrice, è possibile pre-aggregare i dati necessari.Vedere questa risposta in un thread separato:

qual è l'utilità di tipo array?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top