跳到主要内容

什么是倒排索引Inverted Index

· 阅读需 4 分钟
素明诚
Full stack development

假设淘宝上有以下三个商品描述,我们将建立每个词对应的文档列表,如下表所示

商品 1:苹果手机新款发布,支持 5G 网络

商品 2:小米最新 5G 智能手机,性能卓越

商品 3:华为 5G 网络手机,实际 4G 功耗所以续航时间长

构建倒排索引

对每个商品的描述进行分词和处理后,构建如下的倒排索引

词汇商品 ID 列表
苹果1
手机1, 2, 3
新款1
发布1
支持1
5G1, 2, 3
网络1, 3
小米2
最新2
智能2
性能2
卓越2
华为3
实际3
4G3
功耗3
续航3
时间3
3

用户实际搜索

当用户在淘宝搜索“5G 手机”时,搜索系统会进行以下操作:

分词:将“5G 手机”分解为“5G”和“手机”

查找索引:查询倒排索引,找到含有“5G”和“手机”的商品 ID

  1. “5G”对应商品 1, 2
  2. “手机”对应商品 1, 2, 3

合并结果:由于两个词的搜索结果相同,返回商品 1, 2

排序和展示:基于其他因素(如商品评分、销量等)对搜索结果进行排序,并展示给用户

存储和查找

存储:对于需要极高读取性能的应用,倒排索引可以存储在内存中的数据结构,如Redis或者是 map 这种数据结构也可以存储在分布式文件系统Hadoop,或者是搜索引擎专用的Elasticsearch

查找:内存数据库提供极快的数据访问速度,适用于实时搜索和高频访问场景

倒排索引的构建过程

1.文档预处理

去噪声:移除文档中的 HTML 标签、特殊符号、数字等无关内容

标准化:统一文档的格式,如将所有文字转换为小写,统一使用的日期和数字格式等

2.分词(Tokenization)

分词:中文文本的分词比英文复杂,因为中文没有明显的词与词之间的空格分隔因此,需要使用专门的中文分词工具来识别中文句子中的词汇,如结巴分词(jieba)、汉语言处理包(HanLP)等

词干提取(Stemming):将词汇还原为基本形式(例如,“running”还原为“run”)

去停用词:中文的停用词(如“的”、“了”、“在”等)需要特别处理,因为这些词在中文中非常常见,但通常不携带重要信息

编码:确保处理和存储过程中使用统一的字符编码(如 UTF-8),这对于处理中文等多字节语言尤为重要,以防出现乱码问题

查询处理:在处理查询时,同样需要对查询文本进行分词处理,确保查询词与索引中的词能够正确匹配

3.构建倒排记录

创建倒排记录:对于每一个分词,创建一个记录,记录中包含该词出现的所有文档 ID 以及该词在每个文档中的位置

记录更新:随着新文档的添加,不断更新每个词的倒排记录

4.索引存储

索引格式化:将倒排索引以某种格式(如哈希表、B 树)存储在文件或数据库中

压缩:为了节省存储空间和提高查询效率,倒排索引通常会被压缩常用的压缩技术包括编码文档 ID 差值、使用位运算等

5.优化和维护

索引优化:定期优化索引结构,合并小索引文件,删除无用索引,以提高搜索效率

动态更新:支持索引的实时更新,以反映内容的变化

6.索引使用

查询处理:接收用户查询,解析查询词,通过倒排索引快速找到包含这些词的文档

排名算法:基于文档中词的频率、位置等因素对搜索结果进行排名

Go 实现简单倒排索引

package algorithm

type Doc struct {
Id int
Keywords []string
}

func BuildInvertedIndex(docs []Doc) map[string][]int {
index := make(map[string][]int)
for _, doc := range docs {
for _, keyword := range doc.Keywords {
index[keyword] = append(index[keyword], doc.Id)
}
}
return index
}