2015-04-23 71 views
69

我很难理解如何在Android上使用全文搜索(FTS)。我读过SQLite documentation on the FTS3 and FTS4 extensions。我知道it's possible to do on Android。但是,我很难找到任何可以理解的例子。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
  • 对于这样一个大表,将text_column的重复条目存储在FTS表中将是不理想的。建议使用external content table
  • 外部内容表使用FTS4,但FTS4是not supported before Android API 11。答案可以假定API> = 11,但评论支持较低版本的选项会有所帮助。
  • 更改原始表中的数据不会自动更新FTS表(反之亦然)。在你的回答中包括triggers对于这个基本的例子来说并不是必须的,但是仍然会有帮助。
+2

证据充分的问题,我对付你来到这里任意downvote。 – Mekap

回答

92

最基本的答案

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

Create FTS Table

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

这可以去你的外扩SQLiteOpenHelper类的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#insertprepared statementsexecSQL

Query FTS Table

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

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

Fuller答案

上面的虚拟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查询现在在fts_example_table你将不会得到任何结果。原因是更改一个表格不会自动更改其他表格。您必须手动更新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

实际上,如果您按照指定的方式使用fts表(从非fts表中选择其中_id包含在由fts表匹配返回的docid集中),则可以使用content =“ ”。这将创建全文索引而不复制内容。请参阅[无特色FTS4表格](http://www.sqlite.org/fts3.html#section_6_2_1) – astyanaxas

+0

FTS4内容选项的添加时间不早于SQLite 3.7.9(http://www.sqlite.org/releaselog/ 3_7_11.html),这意味着它在Android API 16之前不可用.SQLiteDatabase将抛出使用尝试。 – Knuckles

1

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

我这样做与更新,插入一个触发器,删除

+1

具有代码的详细信息将有所帮助。 – Suragch

+0

'INSERT INTO foo_fts VALUES(“rebuild”)' –