【问题标题】:Lucene 6 - How to influence ranking with numeric value?Lucene 6 - 如何用数值影响排名?
【发布时间】:2019-06-13 00:51:59
【问题描述】:

我是 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 是我要找的东西,但它似乎不起作用。感谢您的帮助!

【问题讨论】:

    标签: java lucene


    【解决方案1】:

    如您链接的博客文章中所示,您可以使用CustomScoreQuery;这会给你很大的灵活性和对评分过程的影响,但这也有点矫枉过正。另一种可能性是使用FunctionScoreQuery;由于它们的行为不同,我将解释两者。

    使用 FunctionScoreQuery

    FunctionScoreQuery 可以根据字段修改分数。

    假设您创建您通常执行这样的搜索:

    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

    【讨论】:

    • 这正是我想要的——谢谢!现在的问题是取得适当的平衡,但这需要更多的实验。
    • 如何使用 FunctionScoreQuery 提升字符串或文本字段(我的意思不是数字)?能否请您也回复一下这个问题:stackoverflow.com/questions/50948894/…
    • @StackUser 只是根据字段值返回一个提升值,使用存储在索引中的确切值只是一个示例。
    猜你喜欢
    • 2011-01-20
    • 2011-11-19
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2017-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多