Android中的全文搜索示例

Posted

技术标签:

【中文标题】Android中的全文搜索示例【英文标题】:Full text search example in Android 【发布时间】:2015-07-01 03:42:53 【问题描述】:

我很难理解如何在 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,但仍然会有所帮助。

【问题讨论】:

有据可查的问题,我反对你在这里得到的任意反对票。 【参考方案1】:

最基本的答案

我使用下面的普通 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#insert 或prepared 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。

创建表格

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

(同样,在 do 插入中有比 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)到外部内容表。这会变得很麻烦。如果你只是制作一个预填充的数据库,你可以这样做

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

这将重建整个表。不过,这可能会很慢,所以这不是您在每次小改动后都想做的事情。您将在完成外部内容表上的所有插入后执行此操作。如果确实需要自动保持数据库同步,可以使用triggers。 Go 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 虚拟表的其他方法:

SQLite FTS3 and FTS4 Extensions(SQLite 文档) Storing and Searching for Data(Android 文档)

附加说明

SQLite FTS 查询中的集合运算符(AND、OR、NOT)有Standard Query Syntax 和Enhanced Query Syntax。不幸的是,Android 显然不支持增强型查询语法(参见here、here、here 和here)。这意味着混合 AND 和 OR 变得困难(似乎需要使用UNION 或检查PRAGMA compile_options)。非常不幸。如果该区域有更新,请添加评论。

【讨论】:

事实上,如果您以您指定的方式使用 fts 表(从非 fts 表中选择,其中 _id 包含在 fts 表匹配返回的 docid 集中),您可以使用 content="" 节省空间。这将创建全文索引而不复制内容。见Contentless FTS4 Tables FTS4 内容选项的添加不早于 SQLite 3.7.9 (sqlite.org/releaselog/3_7_11.html),这意味着它在 Android API 16 之前不可用。SQLiteDatabase 将在尝试使用时抛出。 如何通过此查询获得半字匹配? @HiteshDanidhariya,这不是进行部分单词匹配吗?抱歉,我已经有一段时间没有做这个了,但我认为它已经做到了。 @suragch 得到了解决方案。必须在 searchString 和Thanks 之后添加“*”。您的回答对我帮助很大。 :)【参考方案2】:

在使用 content from 重建 fts 表时不要忘记。

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

【讨论】:

INSERT INTO foo_fts VALUES("rebuild")

以上是关于Android中的全文搜索示例的主要内容,如果未能解决你的问题,请参考以下文章

为啥 SQL Server 全文搜索不匹配数字?

sql MS SQL全文搜索查询示例

Solr 全文搜索

SQL Server中的全文搜索

Lucene及全文搜索实现原理

ElasticSearch 结构化搜索全文