最近、私は Androidアーキテクチャコンポーネント (より具体的には 部屋 )をいじっていますが、少し障害にぶつかりました。
部門とその担当者のリストを格納するRoomデータベースの構築に成功しました。以前は、このデータはサーバーから取得されていましたが、ローカルには保存されていませんでした。検索機能もリモートで処理されていたので、ローカルでも検索機能を処理することを検討していますが、SQLに関する知識が少し不足しています。
サーバー上のSQLコードを見ると、検索ステートメントは一連のREGEXP
関数を使用して、提供されたクエリに基づいて両方のデータベースを検索します。これは検索を処理するための最良の方法とは思えませんが、かなりうまく機能し、迅速な応答を示しました。そこで、これをローカルで模倣しようとしましたが、REGEXP
がAndroid(NDKを使用しない場合)ではサポートされていないことがすぐにわかりました。
LIKE
およびGLOB
演算子に関しては、実行できることは非常に限られているようです。たとえば、一度に複数のキーワードと照合できる方法がわかりません。一方、REGEXP
を使用すると、空白をor
(|
)演算子に置き換えるだけで、この機能を実現できます。
それで、私が出くわした代替案を探しています 全文検索 (FTS);これは 検索の実装に関するAndroidドキュメント で示されている方法です。 FTSは完全なドキュメントを検索するためのもののようですが、私のユースケースのように単純なデータではありません。
いずれにせよ、FTS Roomではサポートされていません 。
したがって、当然のことながら、それを実行する SupportSQLiteOpenHelper.Factory
の実装を作成することにより、Roomに標準テーブルではなくFTS仮想テーブルを作成させようとしました。この実装は、デフォルトのFrameworkSQLiteOpenHelperFactory
および関連するフレームワーククラスのほぼ直接のコピーです。必要なコードはSupportSQLiteDatabase
にあり、execSQL
をオーバーライドして、必要に応じて仮想テーブルコードを挿入します。
class FTSSQLiteDatabase(
private val delegate: SQLiteDatabase,
private val ftsOverrides: Array<out String>
) : SupportSQLiteDatabase {
// Omitted code...
override fun execSQL(sql: String) {
delegate.execSQL(injectVirtualTable(sql))
}
override fun execSQL(sql: String, bindArgs: Array<out Any>) {
delegate.execSQL(injectVirtualTable(sql), bindArgs)
}
private fun injectVirtualTable(sql: String): String {
if (!shouldOverride(sql)) return sql
var newSql = sql
val tableIndex = sql.indexOf("TABLE")
if (tableIndex != -1) {
sql = sql.substring(0..(tableIndex - 1)) + "VIRTUAL " + sql.substring(tableIndex)
val argumentIndex = sql.indexOf('(')
if (argumentIndex != -1) {
sql = sql.substring(0..(argumentIndex - 1) + "USING fts4" + sql.substring(argumentIndex)
}
}
return newSql
}
private fun shouldOverride(sql: String): Boolean {
if (!sql.startsWith("CREATE TABLE")) return false
val split = sql.split('`')
if (split.size >= 2) {
val tableName = split[1]
return ftsOverrides.contains(tableName)
} else {
return false
}
}
}
少し面倒ですが、うまくいきます!さて、それは仮想テーブルを作成します…
しかし、次のSQLiteException
を取得します。
04-04 10:54:12.146 20289-20386/com.example.app E/SQLiteLog: (1) cannot create triggers on virtual tables
04-04 10:54:12.148 20289-20386/com.example.app E/ROOM: Cannot run invalidation tracker. Is the db closed?
Android.database.sqlite.SQLiteException: cannot create triggers on virtual tables (code 1): , while compiling: CREATE TEMP TRIGGER IF NOT EXISTS `room_table_modification_trigger_departments_UPDATE` AFTER UPDATE ON `departments` BEGIN INSERT OR REPLACE INTO room_table_modification_log VALUES(null, 0); END
at Android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at Android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.Java:890)
at Android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.Java:501)
at Android.database.sqlite.SQLiteSession.prepare(SQLiteSession.Java:588)
at Android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.Java:58)
at Android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.Java:31)
at Android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.Java:1752)
at Android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.Java:1682)
at com.example.app.data.FTSSQLiteDatabase.execSQL(FTSSQLiteDatabase.kt:164)
at Android.Arch.persistence.room.InvalidationTracker.startTrackingTable(InvalidationTracker.Java:204)
at Android.Arch.persistence.room.InvalidationTracker.access$300(InvalidationTracker.Java:62)
at Android.Arch.persistence.room.InvalidationTracker$1.run(InvalidationTracker.Java:306)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1162)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:636)
at Java.lang.Thread.run(Thread.Java:764)
Roomはテーブルを作成しますが、仮想テーブルにトリガーを作成しようとします 明らかに許可されていません 。トリガーをオーバーライドしようとすると(つまり、トリガーが実行されないようにするだけで)、Roomの機能の多くが損なわれると思います。これが、RoomがそもそもFTSをサポートしていない理由だと思います。
したがって、RoomがFTSをサポートしておらず(強制的にサポートできない場合)、REGEXP
がサポートされていない場合(NDKを使用しない限り)。 Roomの使用中に検索を実装する別の方法はありますか? FTSは正しい方法でさえありますか(やり過ぎのようです)、または私のユースケースにより適した他の方法はありますか?
私たちはついにそれを手に入れ、バージョンから始めました 2.1.0-alpha01 RoomはマッピングFTS3またはFTS4テーブルを持つエンティティをサポートします。詳細と使用例については、ドキュメントを参照してください: @ Fts および @ Fts4