Androidで全文検索(FTS)を使用する方法を理解するのに苦労しています。 FTS3およびFTS4拡張機能に関するSQLiteドキュメント を読みました。そして、私は Android上で行うことが可能です を知っています。しかし、理解できる例を見つけるのに苦労しています。
SQLiteデータベーステーブル(名前はexample_table
)には4つの列があります。ただし、全文検索のためにインデックスを作成する必要がある列は1つだけです(名前はtext_column
)。 text_column
のすべての行には、長さが0〜1000ワードのテキストが含まれています。行の総数は10,000を超えています。
text_column
でFTSクエリをどのように実行しますか?追記:
example_table
の削除)のみ 非FTSクエリでは非効率 になります。text_column
の重複エントリをFTSテーブルに格納することは望ましくありません。 この投稿 は 外部コンテンツテーブル の使用を提案します。すべてが可能な限り明確で読みやすいように、以下のプレーンなSQLを使用しています。プロジェクトでは、Android簡易メソッドを使用できます。以下で使用されるdb
オブジェクトは、 SQLiteDatabase のインスタンスです。
db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 ( col_1, col_2, text_column )");
これは、拡張SQLiteOpenHelper
クラスのonCreate()
メソッドに入れることができます。
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.')");
execSQL
よりも SQLiteDatabase#insert または prepared statement を使用することをお勧めします。
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_example_table
でFTSクエリを実行しようとした場合、結果は得られません。その理由は、1つのテーブルを変更しても、他のテーブルは自動的に変更されないためです。 FTSテーブルを手動で更新する必要があります。
db.execSQL("INSERT INTO fts_example_table (docid, text_column) SELECT _id, text_column FROM example_table");
(docid
は、通常のテーブルのrowid
のようなものです。)外部コンテンツテーブルに変更(INSERT、DELETE、UPDATE)を行うたびに、FTSテーブルを更新する(インデックスを更新できるようにする)必要があります。 。これは面倒です。事前に作成されたデータベースのみを作成している場合は、次のことができます
db.execSQL("INSERT INTO fts_example_table(fts_example_table) VALUES('rebuild')");
テーブル全体が再構築されます。ただし、これは遅くなる可能性があるため、少し変更するたびにやりたいことではありません。外部コンテンツテーブルへのすべての挿入が完了したら、それを行います。データベースの同期を自動的に維持する必要がある場合は、 triggers を使用できます。 ここに行き 、少し下にスクロールして道順を見つけます。
データベースのクエリ
String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_example_table WHERE fts_example_table MATCH ?", selectionArgs);
これは以前と同じですが、今回はtext_column
(およびdocid
)のみにアクセスできます。外部コンテンツテーブルの他の列からデータを取得する必要がある場合はどうなりますか? FTSテーブルのdocid
は外部コンテンツテーブルの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仮想テーブルを使用する他の方法を確認してください。
UNION
の使用または PRAGMA compile_options
の使用が必要です) 。とても残念です。この領域に更新がある場合は、コメントを追加してください。からのコンテンツを使用してftsテーブルを再構築することを忘れないでください。
更新、挿入、削除のトリガーでこれを行います