Android

时间:2015-04-23 06:18:12

标签: android sqlite full-text-search

我很难理解如何在Android上使用全文搜索(FTS)。我已经阅读了SQLite documentation on the FTS3 and FTS4 extensions。我知道it's possible to do on Android。但是,我很难找到任何我能理解的例子。

基本数据库模型

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

  • 您将如何设置表和/或FTS虚拟表?
  • 您如何在text_column上执行FTS查询?

附加说明:

  • 由于只需要对一列进行索引,因此仅使用FTS表(并删除example_table)将为inefficient for non-FTS queries
  • 对于这样大的表,在FTS表中存储text_column的重复条目是不合需要的。 This post建议使用external content table
  • 外部内容表使用FTS4,但FTS4为not supported before Android API 11。答案可以假设API> = 11,但评论支持较低版本的选项会有所帮助。
  • 更改原始表中的数据不会自动更新FTS表(反之亦然)。对于这个基本示例,在答案中包含triggers不是必需的,但仍然会有所帮助。

2 个答案:

答案 0 :(得分:108)

最基本答案

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

Create FTS Table

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

这可以使用扩展的onCreate()类的SQLiteOpenHelper方法。

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#insertprepared statements而不是execSQL

Query FTS Table

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

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

富勒答案

上面的虚拟FTS表有问题。每列都已编入索引,但如果某些列不需要编入索引,则会浪费空间和资源。唯一需要FTS索引的列可能是text_column

要解决此问题,我们将使用常规表和虚拟FTS表的组合。 FTS表将包含索引,但不包含常规表中的实际数据。相反,它将有一个指向常规表内容的链接。这称为external content table

enter image description here

创建表格

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表(但这意味着数据库将更大,因为全文列存在于两个数据库中。)

填充表格

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

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

docid类似于常规表的rowid。)每次进行更改时,都必须确保更新FTS表(以便它可以更新索引)( INSERT,DELETE,UPDATE)到外部内容表。这可能会很麻烦。如果您只是制作预填充数据库,则可以

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

将重建整个表。但这可能很慢,因此在每次微小变化后都不是你想做的事情。完成外部内容表上的所有插入后,您将执行此操作。如果确实需要自动保持数据库同步,则可以使用triggersGo here然后向下滚动一下以查找路线。

查询数据库

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

这与以前相同,但这次您只能访问text_column(和docid)。如果您需要从外部内容表中的其他列获取数据,该怎么办?由于FTS表的docid与外部内容表的rowid(在本例中为_id)匹配,因此您可以使用联接。 (感谢this answer寻求帮助。)

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虚拟表的其他方法:

附加说明

答案 1 :(得分:2)

使用内容来重建fts表时不要忘记。

我通过更新,插入,删除

上的触发器执行此操作