我是Lucene的新手,对于任何不清楚的措辞,我深表歉意。我正在使用作者搜索引擎。搜索查询是作者姓名。默认搜索结果很好-它们返回最匹配的名称。但是,我们也希望按作者受欢迎程度对结果进行排名,这是默认相似度和代表其标题发行量的数值两者的混合。默认结果的问题在于,它会返回没有人感兴趣的作者,尽管我可以按发行量单独排名,但是就名字而言,最佳结果通常并不匹配。我一直在寻找解决方案。
这就是我建立索引的方式:
IndexWriter writer = new IndexWriter(FSDirectory.open(Paths.get(INDEX_LOCATION)),
new IndexWriterConfig(new StandardAnalyzer()));
writer.deleteAll();
for (Contributor contributor : contributors) {
Document doc = new Document();
doc.add(new TextField("name", contributor.getName(), Field.Store.YES));
doc.add(new StoredField("contribId", contributor.getContribId()));
doc.add(new NumericDocValuesField("sum", sum));
writer.addDocument(doc);
}
writer.close();
名称是我们要搜索的字段,总和是我们要对搜索结果进行加权的字段(但仍要考虑作者姓名的最佳匹配)。我不确定在这种情况下将总和添加到文档中是否正确。我知道将需要进行一些实验,以找出如何最好地融合两个因素的权重,但是我的问题是我一开始不知道该怎么做。
我能够找到的任何示例都是Lucene 4之前的版本,或者似乎不起作用。我以为this是我想要的,但似乎没有用。帮助赞赏!
最佳答案
如您所链接的博客文章所示,您可以使用CustomScoreQuery
;这会给您很大的灵活性,并在计分过程中发挥影响力,但这也有些过分。另一种可能性是使用FunctionScoreQuery
;由于它们的行为不同,因此我将同时解释两者。
使用FunctionScoreQueryFunctionScoreQuery
可以基于字段修改分数。
假设您创建了自己,通常会执行如下搜索:
Query q = .... // pass the user input to the QueryParser or similar
TopDocs hits = searcher.search(query, 10); // Get 10 results
然后,您可以像下面这样修改查询:
Query q = .....
// Note that a Float field would work better.
DoubleValuesSource boostByField = DoubleValuesSource.fromLongField("sum");
// Create a query, based on the old query and the boost
FunctionScoreQuery modifiedQuery = new FunctionScoreQuery(q, boostByField);
// Search as usual
TopDocs hits = searcher.search(query, 10);
这将根据字段的值修改查询。但是,可悲的是,无法控制
DoubleValuesSource
的影响(除了在索引期间缩放值之外)-至少我不知道。要获得更多控制,请考虑使用
CustomScoreQuery
。使用CustomScoreQuery
使用这种查询可以让您以自己喜欢的方式修改每个结果的分数。在这种情况下,我们将使用它根据索引中的字段更改分数。首先,您必须在建立索引期间存储您的值:
doc.add(new StoredField("sum", sum));
然后,我们将不得不创建自己的查询类:
private static class MyScoreQuery extends CustomScoreQuery {
public MyScoreQuery(Query subQuery) {
super(subQuery);
}
// The CustomScoreProvider is what actually alters the score
private class MyScoreProvider extends CustomScoreProvider {
private LeafReader reader;
private Set<String> fieldsToLoad;
public MyScoreProvider(LeafReaderContext context) {
super(context);
reader = context.reader();
// We create a HashSet which contains the name of the field
// which we need. This allows us to retrieve the document
// with only this field loaded, which is a lot faster.
fieldsToLoad = new HashSet<>();
fieldsToLoad.add("sum");
}
@Override
public float customScore(int doc_id, float currentScore, float valSrcScore) throws IOException {
// Get the result document from the index
Document doc = reader.document(doc_id, fieldsToLoad);
// Get boost value from index
IndexableField field = doc.getField("sum");
Number number = field.numericValue();
// This is just an example on how to alter the current score
// based on the value of "sum". You will have to experiment
// here.
float influence = 0.01f;
float boost = number.floatValue() * influence;
// Return the new score for this result, based on the
// original lucene score.
return currentScore + boost;
}
}
// Make sure that our CustomScoreProvider is being used.
@Override
public CustomScoreProvider getCustomScoreProvider(LeafReaderContext context) {
return new MyScoreProvider(context);
}
}
现在,您可以使用新的Query类来修改现有查询,类似于
FunctionScoreQuery
:Query q = .....
// Create a query, based on the old query and the boost
MyScoreQuery modifiedQuery = new MyScoreQuery(q);
// Search as usual
TopDocs hits = searcher.search(query, 10);
结束语
使用
CustomScoreQuery
,您可以通过各种方式影响评分过程。但是请记住,每个搜索结果都会调用customScore
方法-因此不要在其中执行任何昂贵的计算,因为这会严重减慢搜索过程。我在这里创建了
CustomScoreQuery
的完整工作示例的小要点:https://gist.github.com/philippludwig/14e0d9b527a6522511ae79823adef73a