Android 中的全文搜索示例

Full text search example in Android

我很难理解如何通过 Android 使用全文搜索 (FTS)。我读过 SQLite documentation on the FTS3 and FTS4 extensions. And I know it's possible to do on Android。但是,我很难找到任何我能理解的例子。

基本数据库模型

一个 SQLite 数据库 table(名为 example_table)有 4 列。但是,只有一列(名为 text_column)需要为全文搜索编制索引。 text_column 的每一行都包含长度从 0 到 1000 个单词不等的文本。总行数大于10000

补充说明:

最基本的答案

我在下面使用普通的 sql,这样一切都尽可能清晰易读。在您的项目中,您可以使用 Android 便捷方法。下面使用的 db 对象是 SQLiteDatabase.

的实例

Create FTS Table

db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 ( col_1, col_2, text_column )");

这可以放在扩展 SQLiteOpenHelper class 的 onCreate() 方法中。

Populate FTS Table

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.')");

使用 SQLiteDatabase#insert or prepared statementsexecSQL 更好。

Query FTS Table

String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_table WHERE fts_table MATCH ?", selectionArgs);

您也可以使用 SQLiteDatabase#query 方法。请注意 MATCH 关键字。

更完整的答案

上面的虚拟FTS table有问题。每一列都被索引了,但是如果某些列不需要被索引,这是对 space 和资源的浪费。唯一需要 FTS 索引的列可能是 text_column.

为了解决这个问题,我们将结合使用常规 table 和虚拟 FTS table。 FTS table 将包含来自常规 table 的实际数据的索引 none。相反,它将有一个 link 到常规 table 的内容。这称为 external content table.

创建表

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)");

请注意,我们必须使用 FTS4 而不是 FTS3 来执行此操作。 API 版本 11 之前的 Android 不支持 FTS4。您可以 (1) 只为 API >= 11 提供搜索功能,或者 (2) 使用 FTS3 table(但这意味着数据库会更大,因为全文列存在于两个数据库中)。

填写表格

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 查询,您将得不到任何结果。原因是更改一个 table 不会自动更改另一个 table。您必须手动更新 FTS table:

db.execSQL("INSERT INTO fts_example_table (docid, text_column) SELECT _id, text_column FROM example_table");

docid 就像常规 table 的 rowid。)您必须确保更新 FTS table(以便它可以更新索引)每次对外部内容进行更改(插入、删除、更新)时 table。这会变得很麻烦。如果你只是制作一个预填充的数据库,你可以做

db.execSQL("INSERT INTO fts_example_table(fts_example_table) VALUES('rebuild')");

这将重建整个 table。不过,这可能会很慢,因此您不想在每次微小的更改后都这样做。您可以在完成对外部内容 table 的所有插入后执行此操作。如果您确实需要自动保持数据库同步,您可以使用 triggers. Go here 并向下滚动一点以查找方向。

查询数据库

String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_example_table WHERE fts_example_table MATCH ?", selectionArgs);

这和以前一样,除了这次你只能访问text_column(和docid)。如果需要从外部内容table中的其他列获取数据怎么办?由于 FTS table 的 docid 匹配外部内容 table 的 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 虚拟的其他方法 tables:

补充说明

  • SQLite FTS 查询中的集合运算符(AND、OR、NOT)似乎有 Standard Query Syntax and Enhanced Query Syntax. Unfortunately, Android apparently does not support the Enhanced Query Syntax (see here, here, here, and here). That means mixing AND and OR becomes difficult (requiring the use of UNION or checking PRAGMA compile_options。很不幸。如果此区域有更新,请添加评论。

使用来自的内容重建fts时不要忘记table。

我在更新、插入、删除时使用触发器来执行此操作