La risposta più semplice
Sto usando il semplice sql di seguito in modo che tutto sia il più chiaro e leggibile possibile. Nel tuo progetto puoi utilizzare i metodi di convenienza di Android. L' db
oggetto utilizzato di seguito è un'istanza di SQLiteDatabase .
Crea tabella FTS
db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 ( col_1, col_2, text_column )");
Questo potrebbe andare nel onCreate()
metodo della tua SQLiteOpenHelper
classe estesa .
Popolare la tabella FTS
db.execSQL("INSERT INTO fts_table VALUES ('3', 'apple', 'Hello. How are you?')");
db.execSQL("INSERT INTO fts_table VALUES ('24', 'car', 'Fine. Thank you.')");
db.execSQL("INSERT INTO fts_table VALUES ('13', 'book', 'This is an example.')");
Sarebbe meglio usare SQLiteDatabase # insert o istruzioni preparate rispetto a execSQL
.
Query FTS Table
String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_table WHERE fts_table MATCH ?", selectionArgs);
È inoltre possibile utilizzare il metodo di query SQLiteDatabase # . Nota la MATCH
parola chiave.
Risposta più piena
La tabella virtuale FTS sopra ha un problema con esso. Ogni colonna è indicizzata, ma questo è uno spreco di spazio e risorse se alcune colonne non devono essere indicizzate. L'unica colonna che necessita di un indice FTS è probabilmente la text_column
.
Per risolvere questo problema useremo una combinazione di una tabella normale e una tabella FTS virtuale. La tabella FTS conterrà l'indice ma nessuno dei dati effettivi dalla tabella normale. Invece avrà un collegamento al contenuto della tabella normale. Questa è chiamata tabella dei contenuti esterni .
Crea le tabelle
db.execSQL("CREATE TABLE example_table (_id INTEGER PRIMARY KEY, col_1 INTEGER, col_2 TEXT, text_column TEXT)");
db.execSQL("CREATE VIRTUAL TABLE fts_example_table USING fts4 (content='example_table', text_column)");
Notare che dobbiamo usare FTS4 per farlo invece di FTS3. FTS4 non è supportato in Android prima della versione dell'API 11. È possibile (1) fornire solo funzionalità di ricerca per API> = 11 o (2) utilizzare una tabella FTS3 (ma ciò significa che il database sarà più grande perché esiste la colonna di testo completo in entrambi i database).
Popolare le tabelle
db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('3', 'apple', 'Hello. How are you?')");
db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('24', 'car', 'Fine. Thank you.')");
db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('13', 'book', 'This is an example.')");
(Di nuovo, ci sono modi migliori per fare gli inserti che con execSQL
. Lo sto usando solo per la sua leggibilità.)
Se provassi a eseguire una query FTS ora su, fts_example_table
non otterrai risultati. Il motivo è che la modifica di una tabella non cambia automaticamente l'altra tabella. Devi aggiornare manualmente la tabella FTS:
db.execSQL("INSERT INTO fts_example_table (docid, text_column) SELECT _id, text_column FROM example_table");
( docid
È come rowid
per una tabella normale.) Devi assicurarti di aggiornare la tabella FTS (in modo che possa aggiornare l'indice) ogni volta che fai una modifica (INSERT, DELETE, UPDATE) alla tabella del contenuto esterno. Questo può diventare complicato. Se stai solo creando un database precompilato, puoi farlo
db.execSQL("INSERT INTO fts_example_table(fts_example_table) VALUES('rebuild')");
che ricostruirà l'intero tavolo. Questo può essere lento, tuttavia, quindi non è qualcosa che vuoi fare dopo ogni piccolo cambiamento. Lo faresti dopo aver terminato tutti gli inserti nella tabella dei contenuti esterni. Se è necessario mantenere i database sincronizzati automaticamente, è possibile utilizzare i trigger . Vai qui e scorri un po 'verso il basso per trovare le indicazioni stradali.
Interroga i database
String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_example_table WHERE fts_example_table MATCH ?", selectionArgs);
È lo stesso di prima, tranne che questa volta hai accesso solo a text_column
(e docid
). E se hai bisogno di ottenere dati da altre colonne nella tabella del contenuto esterno? Poiché la docid
della tabella FTS corrisponde a rowid
(e in questo caso _id
) della tabella del contenuto esterno, è possibile utilizzare un join. (Grazie a questa risposta per l'aiuto in questo.)
String sql = "SELECT * FROM example_table WHERE _id IN " +
"(SELECT docid FROM fts_example_table WHERE fts_example_table MATCH ?)";
String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery(sql, selectionArgs);
Ulteriori letture
Esamina attentamente questi documenti per vedere altri modi di utilizzare le tabelle virtuali FTS:
Note aggiuntive
- Gli operatori di impostazione (AND, OR, NOT) nelle query SQLite FTS hanno la sintassi di query standard e la sintassi di query avanzata . Sfortunatamente, apparentemente Android non supporta la sintassi delle query avanzata (vedi qui , qui , qui e qui ). Ciò significa che mescolare AND e OR diventa difficile (richiedendo l'uso
UNION
o il controllo PRAGMA compile_options
). Davvero sfortunato. Per favore aggiungi un commento se c'è un aggiornamento in quest'area.