Androidでの全文検索の例


87

Androidで全文検索(FTS)を使用する方法を理解するのに苦労しています。FTS3およびFTS4拡張機能に関するSQLiteのドキュメントを読みました。そして私はAndroidでそれが可能であることを知っています。しかし、理解できる例を見つけるのに苦労しています。

基本的なデータベースモデル

SQLiteデータベーステーブル(名前付きexample_table)には4つの列があります。ただし、text_column全文検索でインデックスを作成する必要がある列(名前付き)は1つだけです。のすべての行にtext_columnは、0〜1000語の長さのテキストが含まれています。行の総数が10,000を超えています。

  • テーブルやFTS仮想テーブルをどのように設定しますか?
  • どのようにFTSクエリを実行しtext_columnますか?

その他の注意事項:

  • インデックスを作成する必要があるのは1つの列のみであるため、FTSテーブルを使用する(および削除するexample_table)だけでは、FTS以外のクエリでは非効率になります。
  • このような大きなテーブルの場合text_column、FTSテーブルにの重複エントリを格納することは望ましくありません。この投稿では、外部コンテンツテーブルの使用を提案しています
  • 外部コンテンツテーブルはFTS4を使用しますが、FTS4はAndroid API11より前はサポートされていません。答えはAPI> = 11と想定できますが、より低いバージョンをサポートするためのオプションについてコメントすることは役に立ちます。
  • 元のテーブルのデータを変更しても、FTSテーブルは自動的に更新されません(その逆も同様です)。この基本的な例では、回答にトリガーを含める必要はありませんが、それでも役立ちます。

3
十分に文書化された質問、私はあなたがここで得た恣意的な反対票に反対しています。
mekap 2015

回答:


117

最も基本的な答え

私は以下のプレーンSQLを使用して、すべてが可能な限り明確で読みやすいようにしています。プロジェクトでは、Androidの便利なメソッドを使用できます。db以下に使用されるオブジェクトは、インスタンスであるSQLiteDatabase

FTSテーブルを作成する

db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 ( col_1, col_2, text_column )");

これonCreate()は、拡張SQLiteOpenHelperクラスのメソッドに含まれる可能性があります。

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.')");

それは使用する方が良いでしょうSQLiteDatabase#挿入またはプリペアドステートメントよりをexecSQL

FTSテーブルのクエリ

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

SQLiteDatabase#queryメソッドを使用することもできますMATCHキーワードに注意してください。

より完全な答え

上記の仮想FTSテーブルには問題があります。すべての列にインデックスが付けられますが、一部の列にインデックスを付ける必要がない場合、これはスペースとリソースの浪費です。FTSインデックスを必要とする唯一の列は、おそらくtext_column。です。

この問題を解決するために、通常のテーブルと仮想FTSテーブルの組み合わせを使用します。FTSテーブルにはインデックスが含まれますが、通常のテーブルの実際のデータは含まれません。代わりに、通常のテーブルのコンテンツへのリンクがあります。これは外部コンテンツテーブルと呼ばれます

ここに画像の説明を入力してください

テーブルを作成する

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)");

これを行うには、FTS3ではなくFTS4を使用する必要があることに注意してください。FTS4は、APIバージョン11より前のAndroidではサポートされていません。(1)API> = 11の検索機能のみを提供するか、(2)FTS3テーブルを使用することができます(ただし、全文列が存在するため、データベースが大きくなります)両方のデータベースで)。

テーブルにデータを入力します

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.')");

(繰り返しになりますが、挿入を行うには、挿入を行うよりも優れた方法がありますexecSQL。読みやすさのために使用しています。)

今すぐFTSクエリを実行しようとすると、fts_example_table結果が得られません。その理由は、一方のテーブルを変更しても、もう一方のテーブルは自動的に変更されないためです。FTSテーブルを手動で更新する必要があります。

db.execSQL("INSERT INTO fts_example_table (docid, text_column) SELECT _id, text_column FROM example_table");

(これdocidrowid通常のテーブルの場合と同様です。)外部コンテンツテーブルに変更(INSERT、DELETE、UPDATE)を行うたびに、必ずFTSテーブルを更新する必要があります(インデックスを更新できるようにするため)。これは面倒になる可能性があります。事前入力されたデータベースのみを作成している場合は、次のことができます。

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

これにより、テーブル全体が再構築されます。ただし、これは遅くなる可能性があるため、少し変更するたびに実行したいことではありません。外部コンテンツテーブルへのすべての挿入が完了した後で実行します。データベースの同期を自動的に維持する必要がある場合は、トリガーを使用できますここに行き、少し下にスクロールして道順を見つけます。

データベースのクエリ

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

これは以前と同じですが、今回はtext_column(とdocid)にしかアクセスできない点が異なります。外部コンテンツテーブルの他の列からデータを取得する必要がある場合はどうなりますか?以来docidFTSテーブルの一致したrowid(この場合には_id、外部コンテンツテーブルの)を、あなたが参加することができます。(それを助けてくれたこの答えに感謝します。)

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);

参考文献

これらのドキュメントを注意深く調べて、FTS仮想テーブルを使用する他の方法を確認してください。

その他の注意事項

  • SQLite FTSクエリの集合演算子(AND、OR、NOT)には、標準クエリ構文拡張クエリ構文があります。残念ながら、Androidは明らかに拡張クエリ構文をサポートしていません(ここここここ、およびここを参照)。つまり、ANDとORの混合が難しくなります(使用が必要になるUNIONか、チェックする必要があるPRAGMA compile_optionsようです)。非常に残念です。この領域に更新がある場合は、コメントを追加してください。

1
実際、指定した方法でftsテーブルを使用している場合(ftsテーブルの一致によって返されるdocidのセットに_idが含まれている非ftsテーブルから選択)、content = ""を使用してスペースを節約できます。 。これにより、コンテンツを複製せずにフルテキストインデックスが作成されます。コンテンツレスFTS4テーブル
astyanaxas 2016

FTS4コンテンツオプションはSQLite3.7.9(sqlite.org/releaselog/3_7_11.html)より前に追加されました。これは、Android API16より前では使用できないことを意味します。SQLiteDatabaseは使用試行をスローします。
ナックル

このクエリを使用して、ハーフワードの一致を取得するにはどうすればよいですか?
Hitesh Danidhariya 2018年

@HiteshDanidhariya、これは部分的な単語マッチングを行いませんか?申し訳ありませんが、私がこれに取り組んでからしばらく経ちましたが、私はすでにそれを行っていると思いました。
スラグチ2018年

@suragch解決策を得ました。searchStringとThanksの後に「*」を追加する必要がありました。あなたの答えは私をとても助けてくれました。:)
Hitesh Danidhariya 2018年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.