2012-07-04 8 views
6

Sto sviluppando un'applicazione Android che deve eseguire la ricerca di sottostringa in una tabella di grandi dimensioni (circa 500'000 voci con nomi di strade e località, quindi solo poche parole per voce).SQLite: ricerca della sottostringa efficiente in una tabella di grandi dimensioni

CREATE TABLE Elements (elementID INTEGER, type INTEGER, name TEXT, data BLOB) 

Si noti che solo il 20% di tutte le voci contiene stringhe nella colonna "nome".

Esecuzione seguente query richiede quasi 2 minuti:

SELECT elementID, name FROM Elements WHERE name LIKE %foo% 

ora cercato di utilizzare FTS3 al fine di accelerare la query. Ciò ha avuto un discreto successo, il tempo di interrogazione è sceso a 1 minuto (sorprendentemente le dimensioni del file del database sono aumentate solo del 5%, che è anche abbastanza buono per il mio scopo).

Il problema è, FTS3 apparentemente non supporta la ricerca sottostringa, vale a dire se voglio trovare "bar" in "foo bar" e "foobar", ho solo "foo bar", anche se ho bisogno di entrambi i risultati.

Quindi, in realtà ho due domande:

  1. è possibile accelerare ulteriormente la query? Il mio obiettivo è di 30 secondi per la query, ma non so se sia realistico ...

  2. Come posso ottenere una ricerca di sottostringa reale utilizzando FTS3?

+0

Ci vuole * un sacco * di triturazione per ottenere la ricerca indicizzata sotto parola ... –

+0

Forse SQLite/FST non è l'approccio migliore in questo caso specifico .. sembra un [sola lettura] [Suffix Tree ] (http://en.wikipedia.org/wiki/Suffix_tree) potrebbe essere più adatto. Sebbene il trucco sia quello di trovarne uno in una libreria/tooling già esistente ;-) –

+0

@pst, Suffix Trees sembra piuttosto interessante, ma sfortunatamente l'approccio SQLite è cruciale per le funzionalità principali della mia applicazione. Tuttavia, la ricerca rapida delle stringhe sarebbe stata "piacevole da avere". ;) – Aletheios

risposta

9

Soluzione 1: Se si può fare ogni personaggio nel database come una singola parola, è possibile utilizzare phrase queries per cercare la sottostringa.

Ad esempio, si supponga "my_table" contiene una singola colonna "persona":

person 
------ 
John Doe 
Jane Doe 

si può cambiare a

person 
------ 
J o h n D o e 
J a n e D o e 

Per cercare la stringa "ohn", usi la frase frase:

SELECT * FROM my_table WHERE person MATCH '"o h n"' 

Attenzione che "JohnD" corrisponderà a "John Doe", che potrebbe non essere desiderato. Per risolvere il problema, cambia il carattere dello spazio nella stringa originale in qualcos'altro.

Ad esempio, è possibile sostituire il carattere di spazio con "$":

person 
------ 
J o h n $ D o e 
J a n e $ D o e 

Soluzione 2: Seguendo l'idea di una soluzione 1, è possibile effettuare ogni personaggio come una singola parola con un costume tokenizer e utilizzare le query a frase per eseguire query sulle sottostringhe.

Il vantaggio rispetto soluzione 1 è che non c'è bisogno di aggiungere spazi nei dati, che può aumentare inutilmente la dimensione del database.

Lo svantaggio è che è necessario implementare il tokenizzatore personalizzato. Fortunatamente, ho one ready for you. Il codice è in C, quindi devi capire come integrarlo con il tuo codice Java.

+0

Grazie per l'idea; sembra promettente. Aggiungere tutti quegli spazi potrebbe far esplodere la dimensione del mio database (che non è quello che voglio), ma farò un tentativo non appena avrò tempo. – Aletheios

+0

Se la dimensione è la tua preoccupazione, controlla la soluzione 2. –

+0

Ho testato la tua prima soluzione ora. Come previsto, le dimensioni del database sono quasi raddoppiate, ma il tempo di query è in un intervallo accettabile (le query "regolari" sono comunque più veloci, ma ovviamente non forniscono tutti i risultati che mi servono). Non ho ancora avuto il tempo di includere la tua seconda soluzione nel mio progetto, ma ho testato l'esempio che hai fornito su GitHub e sembra davvero promettente perché mantiene costante la dimensione del database. Immagino sia il miglior compromesso tra velocità e qualità dei risultati, quindi contrassegnerò la tua risposta come corretta. – Aletheios

-1

non sono sicuro di accelerarlo dal momento che si sta utilizzando SqlLite, ma per sottostringa ricerche, ho fatto le cose come

SET @foo_bar = 'foo bar' 
SELECT * FROM table WHERE name LIKE '%' + REPLACE(@foo_bar, ' ', '%') + '%' 

Naturalmente questo soltanto i ritorni record che contengono la parola "pippo" prima della parola "bar".

3

È necessario aggiungere un indice alla colonna name nel database, che dovrebbe velocizzare notevolmente la query.

Credo SQLite3 supporta sub-stringa corrispondente in questo modo:

SELECT * FROM Elements WHERE name MATCH '*foo*'; 

http://www.sqlite.org/fts3.html#section_3

+0

Ho appena provato i tuoi suggerimenti nell'emulatore Android. La corrispondenza delle sottostringhe in FTS3 sembra funzionare come suggerito, ma la query impiega molto tempo (ho ucciso l'app manualmente dopo 5 minuti). Sfortunatamente un indice sulla colonna "nome" non sembra funzionare, il tempo di interrogazione rimane lo stesso. – Aletheios

+0

Se nella tabella FTS3 sono presenti dati che non è necessario eseguire una ricerca full-text, è possibile che si tenti di rimuoverla. Non l'ho fatto da solo, ma potresti provare a eseguire un comando 'optimize' sul tavolo e vedere se questo accelera le cose: http://www.sqlite.org/fts3.html#optimize – twaddington

+0

Anche testato con' optimize "Ora, le prestazioni sono un po 'migliori, ma non molto. Probabilmente dovrò riconsiderare completamente la funzione di ricerca della mia app ... Segnerò la risposta corretta come i tuoi consigli potrebbero aiutare gli altri con problemi simili. – Aletheios

-1

sto affrontando qualche cosa simile al vostro problema. Ecco il mio suggerimento prova a creare una tabella di traduzione che tradurrà tutte le parole in numeri. Quindi cerca i numeri invece delle parole.

Per favore fatemi sapere se questo sta aiutando.

+0

Questa è un'idea interessante, tuttavia non vedo come questo potrebbe accelerare la ricerca. Nota che una voce nella mia colonna "nome" può contenere più di una sola parola, quindi c'è il problema di memorizzare più numeri in una sola voce. Inoltre, la ricerca della sottostringa è impossibile con le rappresentazioni numeriche per ogni parola. – Aletheios

+0

@Aletheios che ne dici di creare una nuova colonna per ogni parola? E puntando tutte le possibilità dei sub-mondi. –

+0

Scusate per il ritardo nella risposta, sono stato impegnato ultimamente. Con più colonne otterrei un po 'di overhead, perché il numero massimo di parole per voce non è prevedibile. A parte questo, non posso immaginare che questo potrebbe accelerare la ricerca in modo significativo (ogni singola colonna dovrebbe essere cercata in modo indipendente). – Aletheios

Problemi correlati