web-dev-qa-db-fra.com

Exemple de recherche de texte intégral dans Android

J'ai du mal à comprendre comment utiliser la recherche de texte intégral (FTS) avec Android. J'ai lu le documentation SQLite sur les extensions FTS3 et FTS4 . Et je sais il est possible de le faire sur Android . Cependant, j'ai du mal à trouver des exemples que je puisse comprendre.

Le modèle de base de base

Une table de base de données SQLite (nommée example_table) a 4 colonnes. Cependant, il n'y a qu'une seule colonne (nommée text_column) qui doit être indexé pour une recherche en texte intégral. Chaque rangée de text_column contient du texte dont la longueur varie de 0 à 1 000 mots. Le nombre total de lignes est supérieur à 10 000.

  • Comment configureriez-vous la table et/ou la table virtuelle FTS?
  • Comment effectueriez-vous une requête FTS sur text_column?

Notes complémentaires:

  • Comme une seule colonne doit être indexée, utilisez uniquement une table FTS (et supprimez example_table) serait inefficace pour les requêtes non-FTS .
  • Pour une table aussi volumineuse, stocker les entrées dupliquées de text_column dans la table FTS ne serait pas souhaitable. Cet article suggère d'utiliser un table de contenu externe .
  • Les tables de contenu externe utilisent FTS4, mais FTS4 est non pris en charge avant Android API 11 . Une réponse peut supposer une API> = 11, mais commente 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 inversement). Inclure déclencheurs dans votre réponse n'est pas nécessaire pour cet exemple élémentaire, mais serait néanmoins utile.
85
Suragch

Réponse la plus élémentaire

J'utilise la plaine sql ci-dessous pour que tout soit aussi clair et lisible que possible. Dans votre projet, vous pouvez utiliser les méthodes pratiques Android. L'objet db 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 méthode onCreate() de votre classe étendue SQLiteOpenHelper.

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 SQLiteDatabase # insert ou instructions préparées plutôt que execSQL.

Interroger la table FTS

String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_table WHERE fts_table MATCH ?", selectionArgs);

Vous pouvez également utiliser la méthode SQLiteDatabase # query . Notez le mot clé MATCH.

Réponse plus complète

La table FTS virtuelle ci-dessus a un problème avec elle. 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 allons utiliser une combinaison d’une table standard et d’une table FTS virtuelle. La table FTS contiendra l'index mais aucune des données réelles de la table normale. Au lieu de cela, il y aura un lien vers le contenu de la table standard. Ceci s'appelle un table de contenu externe .

enter image description here

Créez les tables

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 faire cela plutôt que FTS3. FTS4 n'est pas pris en charge dans Android avant la version 11 de l'API. Vous pouvez (1) uniquement fournir une fonctionnalité de recherche pour l'API> = 11 ou (2) utiliser une table FTS3 (mais cela signifie que sera plus grande parce que 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, les insertions sont meilleures qu'avec execSQL. Je l'utilise simplement pour sa lisibilité.)

Si vous essayez maintenant de faire une requête FTS sur 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");

(docid est semblable à rowid pour une table ordinaire.) 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 lourd. Si vous ne faites qu'une base de données préremplie, vous pouvez faire

db.execSQL("INSERT INTO fts_example_table(fts_example_table) VALUES('rebuild')");

qui reconstruira toute la table. Cela peut être lent, cependant, alors ce n'est pas quelque chose que vous voulez faire après chaque petit changement. Vous le feriez après avoir terminé toutes les insertions de la table de contenu externe. Si vous devez synchroniser automatiquement les bases de données, vous pouvez utiliser déclencheurs . Allez ici et descendez un peu pour trouver les 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 que précédemment, sauf que cette fois, vous avez uniquement accès à text_column (Et docid). Et si vous avez besoin d'obtenir des données d'autres colonnes de la table de contenu externe? Étant donné que le docid de la table FTS correspond au rowid (et dans ce cas _id) De la table de contenu externe, vous pouvez utiliser une jointure. (Merci à cette réponse pour l'aide avec ça.)

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 découvrir d'autres méthodes d'utilisation des tables virtuelles FTS:

Notes complémentaires

  • Les opérateurs définis (AND, OR, NOT) dans les requêtes SQLite FTS ont syntaxe de requête standard et syntaxe de requête améliorée . Malheureusement, Android ne prend apparemment pas en charge la syntaxe de requête améliorée (voir ici , ici , ici , et ici ). Cela signifie que mélanger AND et OR devient difficile (nécessitant l’utilisation de UNION) _ ou en vérifiant PRAGMA compile_options semble-t-il) Très regrettable, veuillez ajouter un commentaire s’il ya une mise à jour dans ce domaine.
111
Suragch

N'oubliez pas lorsque vous utilisez le contenu de pour reconstruire la table fts.

Je fais cela avec un trigger sur update, insert, delete

3
James Kipling