我使用 FTS4 和分词器来删除变音符号。数据库是用 Room 抽象出来的。
类似:
内容
@Entity(
tableName = "test"
)
public class Test {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
private Long mId;
@ColumnInfo(name = "name")
String mName;
public Test(String name) {
mName = name;
}
public Long getId() {
return mId;
}
public void setId(Long id) {
mId = id;
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
}
FTS
@Entity(tableName = "search_fts")
@Fts4(
contentEntity = Test.class,
tokenizer = FtsOptions.TOKENIZER_UNICODE61,
tokenizerArgs = {
"remove_diacritics=2"
}
)
public class SearchFts {
@PrimaryKey
@ColumnInfo(name = "rowid")
int mRowId;
@ColumnInfo(name = "name")
String mName;
public SearchFts(int rowId, String name) {
mRowId = rowId;
mName = name;
}
public int getRowId() {
return mRowId;
}
public void setRowId(int rowId) {
mRowId = rowId;
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
}
这会产生 SQLite 查询:
CREATE VIRTUAL TABLE IF NOT EXISTS search_fts
USING FTS4(
'name' TEXT,
tokenize=unicode61 'remove_diacritics=2', content='test'
);
一切都适用于较新的 Android 版本(即 API 30),但在旧版本(即 API 25 或 27)上我收到以下错误:
android.database.sqlite.SQLiteException: unknown tokenizer (code 1)
官方文档:
“unicode61”标记生成器从 SQLite 版本 3.7.13 (2012-06-11) 开始可用。 Unicode61 的工作方式非常类似于“简单”,只不过它根据 Unicode 版本 6.1 中的规则进行简单的 unicode 大小写折叠,并且它识别 unicode 空格和标点字符并使用它们来分隔标记。
API和数据库版本:
日志猫:
2022-07-26 11:36:56.532 16176-16229/hr.laserline.osis E/SQLiteLog: (1) 语句在 28 处中止:[CREATE VIRTUAL TABLE IF NOT EXISTS
USING FTS4(search_fts
TEXT, tokenize=unicode61name
, content=remove_diacritics=2
)] 未知分词器test
.... 2022-07-26 11:36:56.539 16176-16229/hr.laserline.osis E/hr.laserline.osis.service.sync.SyncServicePresenter:同步错误!数据库事务回滚... android.database.sqlite.SQLiteException:未知分词器(代码1) 在 android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(本机方法) 在 android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:735) 在 android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754) 在 android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64) 在 android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1754) 在 android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1682) 在 androidx.sqlite.db.framework.FrameworkSQLiteDatabase.execSQL(FrameworkSQLiteDatabase.java:265) 在 hr.laserline.osis.data.db.LlamaRoomDatabase_Impl$1.createAllTables(LlamaRoomDatabase_Impl.java:148) 在 androidx.room.RoomOpenHelper.onCreate(RoomOpenHelper.java:74) 在 androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onCreate(FrameworkSQLiteOpenHelper.java:177) 在 android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:333) 在 android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:238) 在 androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:151) 在 androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:112) 在 androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:706) 在 androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:483) 在 hr.laserline.osis.data.db.dao.ParameterDao_Impl.getSyncNumPerChunk(ParameterDao_Impl.java:610) 在 hr.laserline.osis.data.repositories.ParameterRepository.getSyncNumPerChunk(ParameterRepository.java:72) 在 hr.laserline.osis.service.sync.SyncServiceInteractor.setNumPerChunk(SyncServiceInteractor.java:62) 在 hr.laserline.osis.service.sync.SyncServicePresenter.lambda$startSyncWithErp$0$hr-laserline-osis-service-sync-SyncServicePresenter(SyncServicePresenter.java:98) 在 hr.laserline.osis.service.sync.SyncServicePresenter$$ExternalSyntheticLambda2.get(来源未知:10) 在io.reactivex.rxjava3.internal.operators.observable.ObservableDefer.subscribeActual(ObservableDefer.java:33) 在 io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176) 在 io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) 在io.reactivex.rxjava3.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:38) 在io.reactivex.rxjava3.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:25) 在 java.util.concurrent.FutureTask.run(FutureTask.java:266) 在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 在 java.lang.Thread.run(Thread.java:764) 在 hr.laserline.osis.utils.SingleTaskExecutor$1.run(SingleTaskExecutor.java:118)
有什么建议吗?
这不仅仅是 Room,这是 FTS3-4/unicode61 tokenizer 的普遍问题。
简单来说
tokenize=unicode61
有 Android minSdk = 21
要求。tokenize=unicode61 'remove_diacritics=0|1'
有 Android minSdk = 21
要求。tokenize=unicode61 'remove_diacritics=2'
有 Android minSdk = 30
要求。SQLite 3.27.0(从 Android API 30 开始嵌入)有这一行:
向 FTS3 和 FTS5 添加了 remove_diacritics=2 选项。
请参阅我的博客了解完整详细信息。