Android 用房间实现搜索
最近我一直在胡闹(更具体地说),但我遇到了一些障碍 我已经成功地建立了一个房间数据库,其中存储了部门及其人员的列表。以前,这些数据是从服务器中提取的,但没有存储在本地。搜索功能也是远程处理的,所以现在我也希望在本地处理搜索功能,但我对SQL的知识有点缺乏 查看服务器上的SQL代码,search语句使用一组Android 用房间实现搜索,android,full-text-search,android-sqlite,android-room,android-search,Android,Full Text Search,Android Sqlite,Android Room,Android Search,最近我一直在胡闹(更具体地说),但我遇到了一些障碍 我已经成功地建立了一个房间数据库,其中存储了部门及其人员的列表。以前,这些数据是从服务器中提取的,但没有存储在本地。搜索功能也是远程处理的,所以现在我也希望在本地处理搜索功能,但我对SQL的知识有点缺乏 查看服务器上的SQL代码,search语句使用一组REGEXP函数根据提供的查询搜索两个数据库。这似乎不是处理搜索的最佳方式,但它工作得相当好,并给出了快速响应。因此,我尝试在本地模拟这一点,但很快发现,REGEXP在Android上不受支持(
REGEXP
函数根据提供的查询搜索两个数据库。这似乎不是处理搜索的最佳方式,但它工作得相当好,并给出了快速响应。因此,我尝试在本地模拟这一点,但很快发现,REGEXP
在Android上不受支持(不使用NDK)
至于LIKE
和GLOB
操作符,它们的功能似乎非常有限。例如,我看不到一种方法可以同时匹配多个关键字;而对于REGEXP
,我可以用或(|
)操作符替换空白来实现此功能
因此,寻找我遇到的替代方案(FTS);这是在中演示的方法。尽管FTS似乎是用来搜索完整文档的,而不是像我的用例那样简单的数据
无论如何,金融时报
所以,很自然地,我试图通过创建一个实现FTS虚拟表而不是标准表来强制Room创建一个FTS虚拟表。此实现几乎是默认的FrameworkSQLiteOpenHelperFactory
和相关框架类的直接副本。必要的代码位位于SupportSQLiteDatabase
中,我在其中重写execSQL
,以便在必要时插入虚拟表代码
类FTSqlite数据库(
私有val委托:SQLiteDatabase,
私有值覆盖:数组
):SupportSQLiteDatabase{
//省略的代码。。。
重写sql(sql:字符串){
delegate.execSQL(injectVirtualTable(sql))
}
重写sql(sql:String,bindArgs:Array){
delegate.execSQL(injectVirtualTable(sql)、bindArgs)
}
私有虚拟化(sql:String):String{
如果(!shouldOverride(sql))返回sql
var newSql=sql
val tableIndex=sql.indexOf(“表”)
如果(tableIndex!=-1){
sql=sql.substring(0..(tableIndex-1))+“虚拟”+sql.substring(tableIndex)
val argumentIndex=sql.indexOf(“(”)
if(argumentIndex!=-1){
sql=sql.substring(0..(argumentIndex-1)+“使用fts4”+sql.substring(argumentIndex)
}
}
返回新闻SQL
}
private-fun-shouldOverride(sql:String):布尔值{
如果(!sql.startsWith(“创建表”))返回false
val split=sql.split(“`”)
如果(split.size>=2){
val tableName=split[1]
return.contains(tableName)
}否则{
返回错误
}
}
}
这有点乱,但它可以工作!好吧,它创建了虚拟表
但是我得到了以下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)
04-04 10:54:12.146 20289-20386/com.example.app E/SQLiteLog:(1)无法在虚拟表上创建触发器
04-04 10:54:12.148 20289-20386/com.example.app E/ROOM:无法运行失效跟踪器。数据库是否关闭?
android.database.sqlite.SQLiteException:无法在虚拟表(代码1)上创建触发器,编译时:如果不存在,则创建临时触发器`room\u table\u modification\u TRIGGER\u departments\u UPDATE`在`departments`上更新后开始插入或替换到room\u table\u modification\u日志值(null,0);结束
位于android.database.sqlite.SQLiteConnection.nativePrepareStatement(本机方法)
位于android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:890)
位于android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:501)
位于android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
位于android.database.sqlite.SQLiteProgram.(SQLiteProgram.java:58)
位于android.database.sqlite.SQLiteStatement.(SQLiteStatement.java:31)
位于android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1752)
位于android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1682)
位于com.example.app.data.ftsqlitedatabase.execSQL(ftsqlitedatabase.kt:164)
位于android.arch.persistence.room.invalizationtracker.startTrackingTable(invalizationtracker.java:204)
在android.arch.persistence.room.invalizationtracker.access$300(invalizationtracker.java:62)
在android.arch.persistence.room.invalizationtracker$1.run(invalizationtracker.java:306)
位于java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
位于java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
运行(Thread.java:764)
Room创建了表,但随后尝试在虚拟表上创建触发器。如果我尝试覆盖触发器(即阻止它们执行),我猜这将破坏Room的许多功能。我假设,这就是Room首先不支持FTS的原因
太长,读不下去了
因此,如果Room不支持FTS(我无法强制它),并且不支持REGEXP
(除非我使用NDK);在使用Room时,有没有其他方法可以实现搜索?FTS是正确的方法吗(似乎有些过分了),或者有其他更适合我的用例的方法吗?我可以确认这是有效的。这会让人恼火,但确实有效
首先,您需要创建表。对于初始数据库创建,您可以使用Ro
RoomDatabase.Builder<BookDatabase> b=
Room.databaseBuilder(ctxt.getApplicationContext(), BookDatabase.class,
DB_NAME);
b.addCallback(new Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
db.execSQL("CREATE VIRTUAL TABLE booksearch USING fts4(sequence, prose)");
}
});
BookDatabase books=b.build();
@RawQuery
protected abstract long insert(SupportSQLiteQuery queryish);
void insert(ParagraphEntity entity) {
insert(new SimpleSQLiteQuery("INSERT INTO booksearch (sequence, prose) VALUES (?, ?)",
new Object[] {entity.sequence, entity.prose}));
}
@RawQuery
protected abstract List<BookSearchResult> _search(SupportSQLiteQuery query);
List<BookSearchResult> search(String expr) {
return _search(query(expr));
}
private SimpleSQLiteQuery query(String expr) {
return new SimpleSQLiteQuery("SELECT sequence, snippet(booksearch) AS snippet FROM booksearch WHERE prose MATCH ? ORDER BY sequence ASC",
new Object[] {expr});
}