Android中的全文本搜索示例


87

我很难理解如何在Android上使用全文搜索(FTS)。我已经阅读了有关FTS3和FTS4扩展SQLite文档。而且我知道可以在Android上进行。但是,我很难找到我能理解的任何例子。

基本数据库模型

一个SQLite数据库表(名为example_table)有4列。但是,text_column对于全文搜索,只有一列(名为)需要建立索引。每行text_column包含的文本长度在0到1000个单词之间。行的总数大于10,000。

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

补充说明:

  • 因为只需要对一列进行索引,example_table所以对于非FTS查询,仅使用FTS表(和dropping )效率低下
  • 对于如此大的表,text_column将不希望在FTS表中存储重复的条目。这篇文章建议使用外部内容表
  • 外部内容表使用FTS4,但在Android API 11之前不支持FTS4 。答案可以假定API> = 11,但是对支持较低版本的选项进行注释将很有帮助。
  • 更改原始表中的数据不会自动更新FTS表(反之亦然)。对于此基本示例,在您的答案中包括触发器不是必需的,但是仍然会有所帮助。

3
有据可查的问题,我正在反驳您来到这里的任意否决权。
Mekap 2015年

Answers:


117

最基本的答案

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

创建FTS表

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

这可以在onCreate()扩展SQLiteOpenHelper类的方法中进行。

填写FTS表

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#插入准备语句execSQL

查询FTS表

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表将包含索引,但不包含常规表中的任何实际数据。相反,它将具有到常规表内容的链接。这称为外部内容表

在此处输入图片说明

创建表

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。Android之前的API版本11不支持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查询,fts_example_table将不会获得任何结果。原因是更改一个表不会自动更改另一个表。您必须手动更新FTS表:

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

(这docid类似于rowid常规表。)每次对外部内容表进行更改(INSERT,DELETE,UPDATE)时,必须确保更新FTS表(以便它可以更新索引)。这可能会很麻烦。如果您仅创建一个预先填充的数据库,则可以执行

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

这将重建整个表。但是,这可能会很慢,因此您不需要每次更改后都要做。您需要在完成外部内容表上的所有插入之后执行此操作。如果确实需要自动保持数据库同步,则可以使用触发器转到此处并向下滚动一点以找到路线。

查询数据库

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

这与以前相同,除了这次您只能访问text_column(和docid)。如果您需要从外部内容表中的其他列获取数据怎么办?由于docidFTS表的匹配外部内容表的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虚拟表的其他方式:

补充说明


1
实际上,如果您以指定的方式使用fts表(从fts表匹配返回的docid中包含_id的非fts表中选择),则可以使用content =“”来节省空间。这将创建全文索引,而不会重复内容。见无内容的FTS4表
astyanaxas '16

FTS4内容选项的添加不早于SQLite 3.7.9(sqlite.org/releaselog/3_7_11.html),这意味着它在Android API 16之前不可用。SQLiteDatabase会尝试使用。
指节

如何通过该查询获得半字匹配?
Hitesh Danidhariya

@HiteshDanidhariya,这不是部分单词匹配吗?抱歉,自从我从事此工作已经有一段时间了,但是我认为它已经做到了。
Suragch

@suragch找到了解决方案。必须在searchString和Thanks之后添加“ *”。您的回答对我有很大帮助。:)
Hitesh Danidhariya

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.