我在我的应用程序中实施 fts4 时遇到了一些困难。我的目标是使用最新的 jetpack 组件(包括 Room 数据库)完全在 Kotlin 中构建应用程序。但是,当我查看fts4的文档时,这些示例是用 Java 编写的!
除了相关类的 android 文档和fts3/4 的 sqlite 文档之外,我还搜索了一些不同的教程。值得注意的是,这篇博文和这些幻灯片。到目前为止,我能够找到的所有教程都是用 Java 编写的,并且依赖于数据库迁移来构建 fts^(见下文)所需的虚拟表。尽管如此,我还没有拼凑出一个可行的解决方案。
这是我的代码的最新迭代:
实体:
@Entity(tableName = "items")
data class Item(
@PrimaryKey
@ColumnInfo(name = "rowid")
val rowId: Int,
val qualities: String,
val itemId: String,
val name: String,
val description: String,
val quantity: Int,
) {
override fun toString() = name
}
FTS 实体:
@Entity(tableName = "items_fts")
@Fts4(contentEntity = Item::class, tokenizer = FtsOptions.TOKENIZER_PORTER)
data class ItemFTS(
@PrimaryKey
@ColumnInfo(name = "rowid")
val id: Int,
val qualities: String
) {
override fun toString() = items
}
房间数据库:
@Database(entities = [Item::class, ItemFTS::class], version = 1, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun itemDao(): ItemDao
companion object {
// For Singleton instantiation
@Volatile private var instance: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
return instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
}
// Create and pre-populate the database.
private fun buildDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
WorkManager.getInstance().enqueue(request)
}
})
// **I tried adding in migrations here based on the blog posts attached to this stack overflow
// question but didn't seem to work, even when I incremented the database version.**
.build()
}
}
}
种子数据库工作者:
class SeedDatabaseWorker(
context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
private val TAG by lazy { SeedDatabaseWorker::class.java.simpleName }
override suspend fun doWork(): Result = coroutineScope {
try {
applicationContext.assets.open(ITEM_DATA_FILENAME).use { inputStream ->
JsonReader(inputStream.reader()).use { jsonReader ->
val itemType = object : TypeToken<List<Item>>() {}.type
val itemList: List<Item> = Gson().fromJson(jsonReader, itemType)
val database = AppDatabase.getInstance(applicationContext)
database.itemDao().insertAll(itemList)
Result.success()
}
}
} catch (ex: Exception) {
Log.e(TAG, "Error seeding database", ex)
Result.failure()
}
}
}
项目 DAO:
@Dao
interface ItemDao {
@Query("SELECT * FROM items ORDER BY name")
fun getItems(): LiveData<List<Item>>
@Query("SELECT * FROM items WHERE itemId = :itemId")
fun getItem(itemId: String): LiveData<Item>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(items: List<Item>)
@Query("SELECT * FROM items WHERE rowid IN (SELECT rowid FROM items_fts WHERE qualities MATCH :query) ORDER BY name")
fun searchItems(query: String): LiveData<List<Item>>
}
另外,如果您想知道,我确保将 % 查询注释更改为 *.
^(注意)这不是我有任何经验的事情,可能是我努力想出一个可行的解决方案的部分原因。我有点不清楚 Room 将在幕后做多少工作,以及在生成、填充、索引和一般管理 fts 方面我需要干预多少。以前我的解决方案使用了非 fts 搜索查询,除了查询本身之外不需要任何 SQL。我对 SQL 不是很熟悉,而且我真的不了解迁移如何工作的基础知识。是否可以先构建数据库,然后将其迁移到新版本,而无需先安装应用程序,然后在更新版本中迁移数据库?如果不是,并且需要迁移来创建虚拟 fts 表,你怎么可能发布一个准备好 fts 的新 android 应用程序?!我觉得我的知识存在一些巨大的漏洞,阻碍了我取得任何进展。任何好的建议或提出的解决方案将不胜感激!