如何建立一个简单的倒排索引?

Posted

技术标签:

【中文标题】如何建立一个简单的倒排索引?【英文标题】:How to build a simple inverted index? 【发布时间】:2012-09-12 18:07:57 【问题描述】:

我想建立一个没有任何API的搜索引擎的简单索引功能,例如Lucene。在倒排索引中,我只需要记录每个单词的基本信息,例如docID、位置和频率。

现在,我有几个问题:

    建立倒排索引常用什么样的数据结构?多维列表?

    建立索引后,如何写入文件?文件中的格式是什么?像一张桌子?就像在纸上画一个索引表一样?

【问题讨论】:

【参考方案1】:

你可以在TinySearchEngine看到一个非常简单的倒排索引和搜索实现。

对于您的第一个问题,如果您想构建一个简单的(在内存中)倒排索引,直接的数据结构是这样的哈希映射:

val invertedIndex = new collection.mutable.HashMap[String, List[Posting]]

或Java-esque:

HashMap<String, List<Posting>> invertedIndex = new HashMap<String, List<Postring>>();

哈希将每个术语/单词/标记映射到一个帖子列表。 Posting 只是一个对象,表示文档中出现的单词:

case class Posting(docId:Int, var termFrequency:Int)

为新文档编制索引只是对其进行标记(以标记/单词分隔)并为每个标记在哈希映射的正确列表中插入一个新的 Posting。当然,如果在该特定 docId 中已经存在该术语的 Posting,则可以增加 termFrequency。还有其他方法可以做到这一点。对于内存倒排索引,这是可以的,但对于磁盘索引,您可能希望使用正确的 termFrequency 插入一次 Postings,而不是每次都更新它。

关于你的第二个问题,一般有两种情况:

(1) 你有一个(几乎)不可变的索引。你索引所有数据一次,如果你有新数据,你可以重新索引。例如,无需在一个小时内进行多次实时或索引。

(2) 新文件一直到,您需要尽快搜索新到的文件。

对于情况(1),您至少可以有2个文件:

1 - 倒排索引文件。它为每个术语列出所有Postings(docId/termFrequency 对)。这里以纯文本表示,但通常存储为二进制数据。

 Term1<docId1,termFreq><docId2,termFreq><docId3,termFreq><docId4,termFreq><docId5,termFreq><docId6,termFreq><docId7,termFreq>
 Term2<docId3,termFreq><docId5,termFreq><docId9,termFreq><docId10,termFreq><docId11,termFreq>
 Term3<docId1,termFreq><docId3,termFreq><docId10,termFreq>
 Term4<docId5,termFreq><docId7,termFreq><docId10,termFreq><docId12,termFreq>
 ...
 TermN<docId5,termFreq><docId7,termFreq>

2- 偏移文件。为每个术语存储偏移量以在倒排索引文件中找到其倒排列表。这里我用字符表示偏移量,但您通常会存储二进制数据,因此偏移量将以字节为单位。该文件可以在启动时加载到内存中。当您需要查找术语倒排列表时,您会查找其偏移量并从文件中读取倒排列表。

Term1 -> 0
Term2 -> 126
Term3 -> 222
....

除了这 2 个文件,您还可以(并且通常会)有文件来存储每个术语的 IDF 和每个文档的规范。

对于情况 (2),我将尝试简要解释 Lucene(以及随后的 Solr 和 ElasticSearch)是如何做到的。

文件格式可以和上面解释的一样。主要区别在于,当您在 Lucene 等系统中为新文档编制索引而不是从头开始重建索引时,他们只会使用新文档创建一个新索引。所以每次你必须索引某些东西时,你都会在一个新的分离索引中进行。

要在此“拆分”索引中执行查询,您可以针对每个不同的索引(并行)运行查询并将结果合并在一起,然后再返回给用户。

Lucene 将此称为“小”索引segments

这里明显的问题是你会很快得到很多小片段。为避免这种情况,您需要一个用于合并细分和创建更大细分的策略。例如,如果您有多个N segments,您可以决定将所有小于10 KBs 的段合并在一起。

【讨论】:

对于想知道使用什么语言的人 -- 它是Scala 如果偏移文件非常大,比如 1m 项怎么办?在内存中加载这个文件应该不再是一个好主意,那么如果我们逐行加载偏移文件来查找索引。它会导致性能问题。

以上是关于如何建立一个简单的倒排索引?的主要内容,如果未能解决你的问题,请参考以下文章

正排索引 与 倒排索引

利用MapReduce实现倒排索引

Elasticsearch系列---聚合查询原理

我要对mysql中的数据建立倒排索引应该怎么

京东个性化向量召回算法DPSR

倒排索引