问题 lucene - 给予更多权重,更接近的术语是标题的开头


我理解如何在索引时或查询时提升字段。但是,如何才能增加匹配一个更接近标题开头的术语的分数?

例:

Query = "lucene"

Doc1 title = "Lucene: Homepage"
Doc2 title = "I have a question about lucene?"

我希望第一个文档得分更高,因为“lucene”更接近开头(暂时忽略术语频率)。

我看到如何使用SpanQuery来指定术语之间的接近程度,但我不确定如何使用有关该字段中位置的信息。

我在Java中使用Lucene 4.1。


9026
2018-03-01 10:01


起源

在倒排索引中,术语没有位置,单个术语在文档(字段)中出现多次。我没有看到明显的解决方案。 - Marko Topolnik
可能重复 Lucens做“启动”查询的最佳方式 - mindas
@MarkoTopolnik您可以在lucene中存储位置并知道术语的位置。跨度查询实际上依赖于位置。 SpanFirstQuery似乎很适合这里。 - javanna
@javanna我看不到根据位置得分的证据。你能开导吗? - Marko Topolnik
@MarkoTopolnik看看我的回答,抱歉让你暂停一下;) - javanna


答案:


我会利用一个 SpanFirstQuery,匹配字段开头附近的术语。由于所有跨度查询都依赖于位置,默认情况下在lucene中进行索引时启用。

让我们独立测试它:你只需要提供你的 SpanTermQuery 以及可以找到该术语的最大位置(在我的示例中为一个)。

SpanTermQuery spanTermQuery = new SpanTermQuery(new Term("title", "lucene"));
SpanFirstQuery spanFirstQuery = new SpanFirstQuery(spanTermQuery, 1);

鉴于你的两个文件,这个查询只会找到标题为“Lucene:Homepage”的第一个文件,如果你用它来分析它 StandardAnalyzer

现在我们可以以某种方式结合上述内容 SpanFirstQuery 使用普通文本查询,并使第一个只影响分数。您可以使用a轻松完成 BooleanQuery 并将span查询作为这样的should子句:

Term term = new Term("title", "lucene");
TermQuery termQuery = new TermQuery(term);
SpanFirstQuery spanFirstQuery = new SpanFirstQuery(new SpanTermQuery(term), 1);
BooleanQuery booleanQuery = new BooleanQuery();
booleanQuery.add(new BooleanClause(termQuery, BooleanClause.Occur.MUST));
booleanQuery.add(new BooleanClause(spanFirstQuery, BooleanClause.Occur.SHOULD));

可能有不同的方法来实现相同的,也许使用一个 CustomScoreQuery 也是,或自定义代码来实现评分,但在我看来这是最简单的。

我用来测试它的代码打印执行唯一的输出(包括得分) TermQuery 首先,然后是唯一的 SpanFirstQuery 最后合并 BooleanQuery

------ TermQuery --------
Total hits: 2
title: I have a question about lucene - score: 0.26010898
title: Lucene: I have a really hard question about it - score: 0.22295055
------ SpanFirstQuery --------
Total hits: 1
title: Lucene: I have a really hard question about it - score: 0.15764984
------ BooleanQuery: TermQuery (MUST) + SpanFirstQuery (SHOULD) --------
Total hits: 2
title: Lucene: I have a really hard question about it - score: 0.26912516
title: I have a question about lucene - score: 0.09196242

这是完整的代码:

public static void main(String[] args) throws Exception {

        Directory directory = FSDirectory.open(new File("data"));

        index(directory);

        IndexReader indexReader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);

        Term term = new Term("title", "lucene");

        System.out.println("------ TermQuery --------");
        TermQuery termQuery = new TermQuery(term);
        search(indexSearcher, termQuery);

        System.out.println("------ SpanFirstQuery --------");
        SpanFirstQuery spanFirstQuery = new SpanFirstQuery(new SpanTermQuery(term), 1);
        search(indexSearcher, spanFirstQuery);

        System.out.println("------ BooleanQuery: TermQuery (MUST) + SpanFirstQuery (SHOULD) --------");
        BooleanQuery booleanQuery = new BooleanQuery();
        booleanQuery.add(new BooleanClause(termQuery, BooleanClause.Occur.MUST));
        booleanQuery.add(new BooleanClause(spanFirstQuery, BooleanClause.Occur.SHOULD));
        search(indexSearcher, booleanQuery);
    }

    private static void index(Directory directory) throws Exception {
        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_41, new StandardAnalyzer(Version.LUCENE_41));

        IndexWriter writer = new IndexWriter(directory, config);

        FieldType titleFieldType = new FieldType();
        titleFieldType.setIndexOptions(FieldInfo.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
        titleFieldType.setIndexed(true);
        titleFieldType.setStored(true);

        Document document = new Document();
        document.add(new Field("title","I have a question about lucene", titleFieldType));
        writer.addDocument(document);

        document = new Document();
        document.add(new Field("title","Lucene: I have a really hard question about it", titleFieldType));
        writer.addDocument(document);

        writer.close();
    }

    private static void search(IndexSearcher indexSearcher, Query query) throws Exception {
        TopDocs topDocs = indexSearcher.search(query, 10);

        System.out.println("Total hits: " + topDocs.totalHits);

        for (ScoreDoc hit : topDocs.scoreDocs) {
            Document result = indexSearcher.doc(hit.doc);
            for (IndexableField field : result) {
                System.out.println(field.name() + ": " + field.stringValue() +  " - score: " + hit.score);
            }
        }
    }

10
2018-03-01 20:04



像魅力一样工作! - prestomanifesto
这只会提升第一个位置,对吧?所以得分真的是手工实现的,没有得到的支持 SpanQuery。人们还可以为进一步的位置添加更多的条款,但它会变得非常笨拙,特别是如果两个或更多的术语以这种方式得分。假设我不称之为“明显”的解决方案:) - Marko Topolnik
如果你说,它会提升第一的位置 new SpanFirstQuery(spanTermQuery, 1)。您可以根据需要增加最大位置,不需要添加其他子句。我不知道你的意思是“手工”,我没有手动进行任何得分,我只是使用了lucene暴露在开箱即用的东西。明显与否是个人的。从我的角度来看,这很容易,因为我没有必要围绕相似性/记分员等编写自定义代码。 - javanna
我通过将查询分解为标记然后为每个标记创建一个SpanFirstQuery来增加最大位置(第一个标记为1,第二个标记为2)。它显着提高了我以前的相关性得分,所以我说它效果很好。 - prestomanifesto
太好了,谢谢你的反馈! - javanna


来自“Lucene In Action 2”一书

“Lucene在包中提供了内置查询PayloadTermQuery org.apache.lucene.search.payloads。这个查询就是 像SpanTermQuery一样,它匹配包含指定术语的所有文档 并跟踪 实际发生(跨度) 的比赛。

但是,通过使您能够根据出现的有效负载贡献一个评分因子,它会更进一步 在每个学期的发生。为此,您必须创建自己的Similarity类 定义了scorePayload方法,就像这样“

public class BoostingSimilarity extends DefaultSimilarity {
public float scorePayload(int docID, String fieldName,
int start, int end, byte[] payload,
int offset, int length) {
....
}

上面代码中的“start”只是有效负载的起始位置。有效负载与该术语相关联。因此,起始位置也适用于该术语(至少我相信...)

通过使用上面的代码,但忽略有效负载,您将可以访问得分位置的“开始”位置,然后您可以根据该起始值提高分数。

例如:新分数=原始分数*(1.0f /起始位置)

我希望以上工作,如果你找到任何其他有效的解决方案,请在这里发布..


0
2018-03-01 19:59



听起来很不错但我无法在Lucene 4.1中使用它 - prestomanifesto
对不起误导..我可能知道为什么我不工作? - phani
该 scorePayload() 如果您没有实际有效负载,则不会调用该函数。同样的 PayloadFunction PayloadTermQuery的参数 - sbk