使用 Redis 进行搜索 P153
通过改变程序搜索数据的方式,并使用 Redis 来减少绝大部分基于单词或者关键字进行的内容搜索操作的执行时间。 P154
基本搜索原理 P154倒排索引 (inverted indexes) 是互联网上绝大部分搜索引擎使用的底层结构,它类似于书本末尾的索引。倒排索引从每个被索引的文档里面提取一些单词,并记录包含每个单词的文档集合。 P154
示例
假设有三个文档:
R = "it is what it is"
S = "what is it"
T = "it is a banana"
我们就能得到下面的倒排索引集合:
"a": {2}
"banana": {2}
"is": {0, 1, 2}
"it": {0, 1, 2}
"what": {0, 1}
检索的条件 "what", "is" 和 "it" 将对应这个集合:{0,1} ∩ {0,1,2} ∩ {0,1,2} = {0,1}
可以发现 Redis 的集合和有序集合非常适合处理倒排索引。
基本索引操作
从文档里面提取单词的过程通常被成为语法分析 (parsing) 和标记化 (tokenization) ,这个过程可以产生一系列用于表示文档的标记 (token) ,有时又被成为单词 (word) 。 P155
标记化的一个常见的附加步骤就是移除非用词 (stop word) 。非用词就是那些在文档中频繁出现却没有提供相应信息量的单词,对这些单词进行搜索将返回大量无用的结果。 P155
本书中实现方向索引的逻辑非常简单:
将文档划分为单词,并移除一个字符的单词
对于每个单词获取或创建对应的集合,将当前文档的唯一标识放入集合中
如果需要支持中文等,就不能简单进行英文分词,需要分词器进行处理。第一次接触倒排索引是在 Elasticsearch 中,感兴趣的可以了解 Elasticsearch 中倒排索引的实现以及 IK 中文分词器。
基本搜索操作
在索引里面查找一个单词是非常容易的,只需要获取单词集合里面的所有文档即可。根据多个单词查找文档时,就需要根据条件处理对应的集合,再从最终集合中获取所有文档。 P156
可以使用 Redis 的集合操作完成对不同条件的处理:
SINTER / SINTERSTORE: 找出同时包含所有指定单词的文档集合
SUNION / SUNIONSTORE: 找出至少包含一个指定单词的文档集合
SDIFF / SDIFFSTORE: 找出包含某个单词且包含其他某些单词的文档集合
通过以上三类命令,我们基本能实现条件大部分的与或非操作。
分析并执行搜索
我们使用的查询语句进行分词后具有以下特征:
以 + 开头的单词:表示这个单词是前一个单词的同义词,需要取并集
以 - 开头的单词:表示这个单词不希望包含在文档中,需要取差集
其他普通单词:表示用户需要查询这个单词,需要取交集
即: "connect +connection chat -proxy -proxies" 表示查询的文档需要包含 "connect" 或 "connection" ,同时也要包含 "chat" ,并且不能包含 "proxy" 和 "proxies" 。
实际处理时,先对同义词组分别取并集,然后与需要查询的单词一起取交集,最后与不希望包含的单词取差集,这样所得到的集合就是用户查询的结果集。
对搜索结果进行排序和分页 P160上述搜索功能以及能够搜索出用户查询的所有文档唯一标识的集合,现在我们将根据这个文档唯一标识集合以及每个文档的具体信息进行排序分页。
文档唯一标识集合: 存储每个文档的唯一标识,例如: {1, 2, 276}
每个文档的具体信息: 数据结构为 HASH, 以 doc_{id} 为键,内部存储对应文档的相关信息,例如: "doc:276": {"id": 276, "created": 1324114412, "updated": 132562777, "title": "Troubleshooting...", ...}
对于这种情况我们可以使用 Redis 的 SORT 命令对文档唯一标识集合通过引用每个文档的具体信息进行排序分页。 (05. Redis 其他命令简介)
有序索引 P162上面介绍了使用 Redis 进行搜索,并通过引用存储在 HASH 里面的数据对搜索结果进行排序分页。接下来将介绍利用集合和有序集合实现基于多个分值的复合排序操作,它能提供比 SORT 命令更高的灵活性。 P162
对多个数值字段进行排序 P162假设我们目前需要根据文档对更新时间和得票数进行排序,为此我们需要用两个有序集合存储相关信息。这两个有序集合的成员都是文档唯一标识,成员的分值则分别是文档的更新时间和得票数。
设经过搜索后满足搜索条件的文档唯一标识集合为 filtered_doc_ids ,文档唯一标识及其更新时间对应的有序集合为 doc_ids_with_update ,文档唯一 标识及其得票数对应的有序集合为 doc_ids_with_votes 。那么可以通过 ZINTERSTORE 命令对这三个集合求交集,最后得出的满足搜索条件的文档唯一标识及其排序分值对应的有序集合,再使用 ZRANGE, ZREVRANGE 进行分页获取即可。 P162
示例: ZINTERSTORE filtered_doc_ids_with_sort_score 3 filtered_doc_ids doc_ids_with_update doc_ids_with_votes WEIGHTS 0 {update_weight} {vote_weight}
其中:
filtered_doc_ids_with_sort_score 为结果有序集合
filtered_doc_ids 的权重为 0 ,仅用做筛选结果,不用于排序