Android中的全文search示例

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

基本的数据库模型

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

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

补充笔记:

  • 因为只有一列需要被索引,所以对于非FTS查询 ,只使用FTS表(并且丢弃example_table )将是低效的 。
  • 对于如此大的表格,在FTS表中存储重复的text_column条目将是不可取的。 这篇文章build议使用外部内容表 。
  • 外部内容表使用FTS4,但在Android API 11之前不支持 FTS4。 答案可以假设一个API> = 11,但评论支持较低版本的选项将是有帮助的。
  • 更改原始表中的数据不会自动更新FTS表(反之亦然)。 在你的答案中包括触发器对于这个基本的例子来说是不必要的,但是仍然会有帮助。

最基本的答案

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

创buildFTS表

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

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

填充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#insert或准备好的语句而不是execSQL

查询FTS表

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

你也可以使用SQLiteDatabase#查询方法。 注意MATCH关键字。

更好的答案

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

为了解决这个问题,我们将使用常规表格和虚拟FTS表格的组合。 FTS表将包含索引,但不包含常规表中的实际数据。 相反,它将链接到常规表格的内容。 这被称为外部内容表 。

在这里输入图像描述

创build表格

 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。 您可以(1)仅为API> = 11提供searchfunction,或者(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 。)每次对外部内容表进行更改(INSERT,DELETE,UPDATE)时,都必须确保更新FTS表(以便它可以更新索引) 。 这可能会很麻烦。 如果你只是做一个预先填充的数据库,你可以做

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

这将重build整个桌子。 但是,这可能会很慢,所以在每一个小改动之后,你都不想做这件事。 在完成外部内容表中的所有插入之后,您可以执行此操作。 如果你确实需要保持数据库自动同步,你可以使用触发器 。 去这里 ,向下滚动一下,find方向。

查询数据库

 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 )匹配,因此可以使用docid 。 (感谢这个答案 ,帮助。)

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

  • SQLite FTS3和FTS4扩展 (SQLite文档)
  • 存储和search数据 (Android文档)

补充笔记

  • 在SQLite FTS查询中设置运算符(AND,OR,NOT)具有标准查询语法和增强查询语法 。 不幸的是,Android显然不支持增强查询语法(请参阅这里 , 在 这里 , 这里 )。 这意味着混合AND和OR变得困难(需要使用UNION或者检查PRAGMA compile_options )。 很不幸。 如果此区域有更新,请添加评论。

使用内容重buildfts表时不要忘记。

我用触发器更新,插入,删除