我正在尝试构建自己的搜索引擎进行实验。
我知道倒排索引。例如,在索引单词时。
键是单词,并且包含包含该单词的文档ID列表。因此,当您搜索该单词时,您可以立即获得文档
它如何适用于多个单词
你得到每个单词的所有文件并遍历那些文件,看看是否有两个单词?
我觉得情况并非如此。
任何人都可以在没有推测的情况下知道真正的答案吗?
答案 0 :(得分:1)
您需要将单词的位置存储在索引文件中的文档中。 您的索引文件结构应该是这样的.. word id - doc id- no。击中命中率。
现在假设查询包含4个单词“w1 w2 w3 w4”。选择包含大部分单词的文件。现在计算它们在文档中的相对距离。大多数单词出现的文档及其相对距离最小将在搜索结果中具有高优先级。
我开发了一个完整的搜索引擎,而不使用互联网上提供的任何抓取或索引工具。您可以在此处阅读详细说明 - Search Engine
了解更多信息请参阅Google创始人撰写的这篇论文 - click here
答案 1 :(得分:0)
你可以找到biziclop所说的文档集的交集,你可以用相当快的方式完成它。有关更正式的说明,请参阅this post及其中的相关文章。
答案 2 :(得分:0)
正如biziclop所指出的,对于AND查询,您需要将两个查询词的匹配列表(也称为反向列表)相交。
在典型的实现中,实现反转列表使得可以非常有效地(通常在对数时间)搜索任何给定的文档id。实现此目的的一种方法是让它们排序(并使用二进制搜索),但请注意,这并非易事,因为还需要以压缩形式存储它们给定一个查询A AND B
,并假设有一个occ(A)匹配A和occ(B)匹配B(即occ(x):=术语x的匹配列表的长度) 。在不失一般性的情况下,假设occ(A)> occ(B),即A在文档中比B更频繁地出现。然后你要做的是迭代B的所有匹配并在列表中搜索它们中的每一个。如果确实可以在对数时间搜索列表,这意味着你需要
occ(B) * log(occ(A))
计算步骤以识别包含两个术语的所有匹配。
一本描述实施各方面的好书是Managing Gigabytes。
答案 3 :(得分:0)
使用曲折算法,反转索引非常有效地获取交叉点:
假设您的字词是T
列表:
lastDoc <- 0 //the first doc in the collection
currTerm <- 0 //the first term in T
while (lastDoc != infinity):
if (currTerm > T.last): //if we have passed the last term:
insert lastDoc into result
currTerm <- 0
lastDoc <- lastDoc + 1
continue
docId <- T[currTerm].getFirstAfter(lastDoc-1)
if (docID != lastDoc):
lastDoc <- docID
currTerm <- 0
else:
currTerm <- currTerm + 1
此算法假设有效getFirstAfter()
,它可以为您提供符合该术语的第一个文档,并且他的docId大于指定的参数。如果没有,它应该返回无穷大。
如果对术语进行排序,使得最稀有的术语是第一个,则该算法将是最有效的。
该算法最多可确保#docs_matching_first_term * #terms
次迭代,但实际上 - 迭代次数通常会少得多。
注意:虽然这种算法很有效,但AFAIK lucene不使用它。
更多信息可以在this lecture notes幻灯片11-13中找到[在演讲的第一页复制权]
答案 4 :(得分:-1)
我真的不明白为什么人们会谈论这个问题。
Lucene支持使用BooleanQuery组合查询,如果必须,可以无限期地嵌套。
QueryParser还支持AND关键字,这需要两个单词都在文档中。
示例(Lucene.NET,C#):
var outerQuery + new BooleanQuery();
outerQuery.Add(new TermQuery( new Term( "FieldNameToSearch", word1 ) ), BooleanClause.Occur.MUST );
outerQuery.Add(new TermQuery( new Term( "FieldNameToSearch", word2 ) ), BooleanClause.Occur.MUST );
如果您想使用相同的分析器分割单词(您的实际搜索词),也有办法。虽然,QueryParser可能更容易使用。
您可以查看此答案,例如关于如何使用您用于索引的相同分析器拆分字符串: