J'ai du mal à comprendre comment utiliser la recherche en texte intégral (FTS) avec Android. J'ai lu la documentation SQLite sur les extensions FTS3 et FTS4 . Et je sais que c'est possible de le faire sur Android . Cependant, j'ai du mal à trouver des exemples que je peux comprendre.
Le modèle de base de données de base
Une table de base de données SQLite (nommée example_table
) comporte 4 colonnes. Cependant, une seule colonne (nommée text_column
) doit être indexée pour une recherche en texte intégral. Chaque ligne de text_column
contient du texte dont la longueur varie de 0 à 1000 mots. Le nombre total de lignes est supérieur à 10 000.
- Comment configureriez-vous la table et / ou la table virtuelle FTS?
- Comment exécuteriez-vous une requête FTS
text_column
?
Notes complémentaires:
- Étant donné qu'une seule colonne doit être indexée, seule l'utilisation d'une table FTS (et la suppression
example_table
) serait inefficace pour les requêtes non FTS . - Pour une table aussi grande, le stockage d'entrées en double
text_column
dans la table FTS ne serait pas souhaitable. Cet article suggère d'utiliser une table de contenu externe . - Les tables de contenu externes utilisent FTS4, mais FTS4 n'est pas pris en charge avant l'API Android 11 . Une réponse peut supposer une API> = 11, mais commenter les options de prise en charge des versions inférieures serait utile.
- La modification des données dans la table d'origine ne met pas automatiquement à jour la table FTS (et vice versa). L'inclusion de déclencheurs dans votre réponse n'est pas nécessaire pour cet exemple de base, mais serait néanmoins utile.
android
sqlite
full-text-search
Suragch
la source
la source
Réponses:
Réponse la plus basique
J'utilise le SQL clair ci-dessous pour que tout soit aussi clair et lisible que possible. Dans votre projet, vous pouvez utiliser les méthodes pratiques Android. L'
db
objet utilisé ci-dessous est une instance de SQLiteDatabase .Créer une table FTS
db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 ( col_1, col_2, text_column )");
Cela pourrait aller dans la
onCreate()
méthode de votreSQLiteOpenHelper
classe étendue .Remplir la table 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.')");
Il serait préférable d'utiliser des instructions SQLiteDatabase # insert ou préparées que
execSQL
.Table FTS de requête
String[] selectionArgs = { searchString }; Cursor cursor = db.rawQuery("SELECT * FROM fts_table WHERE fts_table MATCH ?", selectionArgs);
Vous pouvez également utiliser la méthode de requête SQLiteDatabase # . Notez le
MATCH
mot - clé.Réponse plus complète
La table FTS virtuelle ci-dessus présente un problème. Chaque colonne est indexée, mais c'est un gaspillage d'espace et de ressources si certaines colonnes n'ont pas besoin d'être indexées. La seule colonne qui nécessite un index FTS est probablement le
text_column
.Pour résoudre ce problème, nous utiliserons une combinaison d'une table régulière et d'une table FTS virtuelle. La table FTS contiendra l'index mais aucune des données réelles de la table régulière. Au lieu de cela, il aura un lien vers le contenu de la table régulière. C'est ce qu'on appelle une table de contenu externe .
Créer les tableaux
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)");
Notez que nous devons utiliser FTS4 pour ce faire plutôt que FTS3. FTS4 n'est pas pris en charge dans Android avant la version 11 de l'API. Vous pouvez soit (1) fournir uniquement une fonctionnalité de recherche pour API> = 11, ou (2) utiliser une table FTS3 (mais cela signifie que la base de données sera plus grande car la colonne de texte intégral existe dans les deux bases de données).
Remplir les tableaux
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.')");
(Encore une fois, il existe de meilleures façons de faire des insertions qu'avec
execSQL
. Je ne l'utilise que pour sa lisibilité.)Si vous essayez de faire une requête FTS maintenant,
fts_example_table
vous n'obtiendrez aucun résultat. La raison en est que la modification d'une table ne modifie pas automatiquement l'autre table. Vous devez mettre à jour manuellement la table FTS:db.execSQL("INSERT INTO fts_example_table (docid, text_column) SELECT _id, text_column FROM example_table");
( Cela
docid
ressemblerowid
à une table normale.) Vous devez vous assurer de mettre à jour la table FTS (afin qu'elle puisse mettre à jour l'index) chaque fois que vous apportez une modification (INSERT, DELETE, UPDATE) à la table de contenu externe. Cela peut devenir fastidieux. Si vous créez uniquement une base de données préremplie, vous pouvez fairedb.execSQL("INSERT INTO fts_example_table(fts_example_table) VALUES('rebuild')");
qui reconstruira toute la table. Cela peut être lent, cependant, ce n'est donc pas quelque chose que vous voulez faire après chaque petit changement. Vous le feriez après avoir terminé toutes les insertions sur la table de contenu externe. Si vous avez besoin de synchroniser automatiquement les bases de données, vous pouvez utiliser des déclencheurs . Allez ici et faites défiler un peu pour trouver des directions.
Interroger les bases de données
String[] selectionArgs = { searchString }; Cursor cursor = db.rawQuery("SELECT * FROM fts_example_table WHERE fts_example_table MATCH ?", selectionArgs);
C'est la même chose qu'avant, sauf que cette fois, vous n'avez accès qu'à
text_column
(etdocid
). Que faire si vous avez besoin d'obtenir des données à partir d'autres colonnes de la table de contenu externe? Puisque ledocid
de la table FTS correspond aurowid
(et dans ce cas_id
) de la table de contenu externe, vous pouvez utiliser une jointure. (Merci à cette réponse pour votre aide.)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);
Lectures complémentaires
Parcourez attentivement ces documents pour voir d'autres façons d'utiliser les tables virtuelles FTS:
Notes complémentaires
UNION
ou la vérificationPRAGMA compile_options
semble-t-il). Très malheureux. Veuillez ajouter un commentaire s'il y a une mise à jour dans ce domaine.la source
N'oubliez pas lorsque vous utilisez le contenu de pour reconstruire la table fts.
Je fais cela avec un déclencheur sur la mise à jour, l'insertion, la suppression
la source
INSERT INTO foo_fts VALUES("rebuild")