我对我正在观察的一些Lucene.NET行为感到困惑。我认为在Java的Lucene中也是如此,但尚未验证。这是一个测试来证明:
[Fact]
public void repro()
{
var directory = new RAMDirectory();
var analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
float firstScore, secondScore, thirdScore;
using (var indexWriter = new IndexWriter(directory, analyzer, IndexWriter.MaxFieldLength.UNLIMITED))
{
var document = new Document();
document.Add(new Field("id", "abc", Field.Store.YES, Field.Index.NOT_ANALYZED));
document.Add(new Field("field", "some text in the field", Field.Store.NO, Field.Index.ANALYZED));
indexWriter.UpdateDocument(new Term("id", "abc"), document, analyzer);
// the more times I call UpdateDocument here, the higher the score is for the subsequent hit
// indexWriter.UpdateDocument(new Term("id", "abc"), document, analyzer);
indexWriter.Commit();
var queryParser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, "field", analyzer);
var parsedQuery = queryParser.Parse("some text in the field");
using (var indexSearcher = new IndexSearcher(directory, readOnly: true))
{
var hits = indexSearcher.Search(parsedQuery, 10);
Assert.Equal(1, hits.TotalHits);
firstScore = hits.ScoreDocs[0].Score;
}
using (var indexSearcher = new IndexSearcher(directory, readOnly: true))
{
var hits = indexSearcher.Search(parsedQuery, 10);
Assert.Equal(1, hits.TotalHits);
secondScore = hits.ScoreDocs[0].Score;
}
document = new Document();
document.Add(new Field("id", "abc", Field.Store.YES, Field.Index.NOT_ANALYZED));
document.Add(new Field("field", "some changed text in the field", Field.Store.NO, Field.Index.ANALYZED));
// if I call DeleteAll here, then score three is the same as score one and two (which is probably fine, though not quite what I expected either)
// indexWriter.DeleteAll();
indexWriter.UpdateDocument(new Term("id", "abc"), document, analyzer);
indexWriter.Commit();
using (var indexSearcher = new IndexSearcher(directory, readOnly: true))
{
var hits = indexSearcher.Search(parsedQuery, 10);
Assert.Equal(1, hits.TotalHits);
thirdScore = hits.ScoreDocs[0].Score;
}
}
// this is fine
Assert.Equal(firstScore, secondScore);
// this is not
Assert.True(thirdScore < secondScore);
}
步骤如下:
firstScore
和secondScore
thirdScore
真正奇怪的是thirdScore
更大而不是firstScore
和secondScore
。这是我发现的:
UpdateDocument
的次数越多,得分就越高RemoveDocument
,而是手动删除和添加文档没有区别WaitForMerges
没有区别任何人都可以向我解释这种行为吗?当文档内容和查询都没有改变时,为什么分数会随着文档的后续更新而改变?
答案 0 :(得分:3)
首先,在尝试理解为什么以某种方式得分时应该注意的最有用的工具:IndexSearcher.Explain
Explanation explain = indexSearcher.Explain(parsedQuery, hits.ScoreDocs[0].Doc);
这些内容为我们详细解释了该分数是如何得出的。在这种情况下,两个不同的评分查询看起来非常相似除了第三个查询的idf分数如下所示:
0.5945349 = idf(docFreq = 2,maxDocs = 2)
与前两个查询相比:
0.3068528 = idf(docFreq = 1,maxDocs = 1)
Lucene更新只是删除后插入。删除通常只标记要删除的文档,并等到以后实际从索引中清除数据。因此,您不会在搜索结果中看到已删除的文档,但它们仍会影响docfreq等统计信息。当您拥有大量数据时,影响通常非常小。
您可以强制索引到ExpungeDeletes
以查看:
indexWriter.UpdateDocument(new Term("id", "abc"), document, analyzer);
indexWriter.Commit();
//arugment=true to block until completed.
indexWriter.ExpungeDeletes(true);
indexWriter.Commit();
然后你应该看到他们都得到相同的分数。
请记住,删除删除操作可能是一项非常昂贵的操作。在实践中,您可能不在每次更新后都这样做。
至于为什么你得到的文件得分与#34;字段中的一些文字&#34;并且在#34;字段中有一些更改文本&#34;,您指的是lengthNorm分数因子。 lengthNorm在索引时计算,并存储在字段的范数中,并且规范以非常有损的方式压缩,直到单字节,以提高性能。总而言之,它们具有三位精度,甚至不是一个有效的十进制数字。因此,在得分中表示的那两者之间没有足够的差异。尝试使用类似的东西:
在字段中更显着地改变了一些文本
你应该看到lengthNorm生效。