通配符匹配的分数与完全匹配之间存在不匹配

时间:2012-03-09 10:34:28

标签: java search lucene scoring

通配符匹配得分与完全匹配

之间存在不匹配

我搜索

recording:live OR recording:luve* 

这是搜索的解释输出

DocNo:0:1.4196585:11111111-1cf0-4d1f-aca7-2a6f89e34b36
1.4196585 = (MATCH) max plus 0.1 times others of:
  0.3763506 = (MATCH) ConstantScore(recording:luve*), product of:
    1.0 = boost
    0.3763506 = queryNorm
  1.3820235 = (MATCH) weight(recording:luve in 0), product of:
    0.7211972 = queryWeight(recording:luve), product of:
      1.9162908 = idf(docFreq=1, maxDocs=5)
      0.3763506 = queryNorm
    1.9162908 = (MATCH) fieldWeight(recording:luve in 0), product of:
      1.0 = tf(termFreq(recording:luve)=1)
      1.9162908 = idf(docFreq=1, maxDocs=5)
      1.0 = fieldNorm(field=recording, doc=0)

DocNo:1:0.3763506:22222222-1cf0-4d1f-aca7-2a6f89e34b36
0.3763506 = (MATCH) max plus 0.1 times others of:
  0.3763506 = (MATCH) ConstantScore(recording:luve*), product of:
    1.0 = boost
    0.3763506 = queryNorm

在我的测试中,我有5个文档,一个包含完全匹配,另一个包含通配符匹配,另外三个不匹配全部。对于通配符匹配,精确匹配的得分 1.4 0.37 相比,几乎是 4 的因子。使用更大的索引,与通配符搜索相比,罕见术语的完全匹配得分会更高。

整个差异是由于用于通配符与精确匹配的得分机制不同,通配符不考虑tf / idf或lengthnorm,因为每次比赛都得到一个常数得分。现在我对我的数据域中的tf或lengthnorm并不感到困扰它并没有太大的区别,但 idf 得分是一个真正的杀手。因为匹配文档在5个文档中找到一次,其idf贡献是idf平方,即 3.61

我知道这个常数得分比计算每个通配符匹配的tf * idf * lengthnorm要快,但我觉得idf对得分贡献如此之大是没有意义的。我也知道我可以改变重写方法,但这有两个问题。

  1. 评分重写方法执行得不太好,因为它们正在计算idf,tf和lengthnorm。 idf是我唯一需要的值。

  2. 那些计算分数的人也没有多大意义,因为他们会计算匹配词的idf,即使这不是实际搜索的内容,这个词可能比我实际搜索的内容更少见因为,可能会提高它高于完全匹配。

  3. (我也可以更改相似度类来覆盖idf计算,所以它总是返回1但这没有意义,因为idf对于将完全匹配与不同单词进行比较非常有用

    即录音:luve OR录音:luve *或录音:OR录音:*

    我希望 luve 的匹配得分高于常用字的匹配

    所以重写方法已经存在或者可能只计算它试图匹配的术语的idf,所以例如在这种情况下我搜索'luve'并且通配符匹配'luvely'它会通过luve(3.61)的idf多次匹配。这样我的通配符匹配就可以与完全匹配相提并论,我可以更改我的查询以略微提升完全匹配,因此完全匹配总是会得分高于通配符但不会高得多

    recording:live^1.2 OR recording:luve* 
    

    并使用这种神秘的重写方法,这将给出(取决于queryNorm):

    • Doc 0:0:1.692
    • Doc 1:0:1.419

2 个答案:

答案 0 :(得分:0)

答案 1 :(得分:0)

好的将此设置为前缀查询的重写方法似乎可以正常工作

public static class MultiTermUseIdfOfSearchTerm<Q extends Query> extends TopTermsRewrite<BooleanQuery> {

    //public static final class MultiTermUseIdfOfSearchTerm extends TopTermsRewrite<BooleanQuery> {
        private final Similarity similarity;

        /**
         * Create a TopTermsScoringBooleanQueryRewrite for
         * at most <code>size</code> terms.
         * <p>
         * NOTE: if {@link BooleanQuery#getMaxClauseCount} is smaller than
         * <code>size</code>, then it will be used instead.
         */
        public MultiTermUseIdfOfSearchTerm(int size) {
            super(size);
            this.similarity = new DefaultSimilarity();

        }

        @Override
        protected int getMaxSize() {
            return BooleanQuery.getMaxClauseCount();
        }

        @Override
        protected BooleanQuery getTopLevelQuery() {
            return new BooleanQuery(true);
        }

        @Override
        protected void addClause(BooleanQuery topLevel, Term term, float boost) {
            final Query tq = new ConstantScoreQuery(new TermQuery(term));
            tq.setBoost(boost);
            topLevel.add(tq, BooleanClause.Occur.SHOULD);
        }

        protected float getQueryBoost(final IndexReader reader, final MultiTermQuery query)
                throws IOException {
            float idf = 1f;
            float df;
            if (query instanceof PrefixQuery)
            {
                PrefixQuery fq = (PrefixQuery) query;
                df = reader.docFreq(fq.getPrefix());
                if(df>=1)
                {
                    idf = (float)Math.pow(similarity.idf((int) df, reader.numDocs()),2);
                }
            }
            return idf;
        }

        @Override
        public BooleanQuery rewrite(final IndexReader reader, final MultiTermQuery query) throws IOException {
            BooleanQuery  bq = (BooleanQuery)super.rewrite(reader, query);

            float idfBoost = getQueryBoost(reader, query);
            Iterator<BooleanClause> iterator = bq.iterator();
            while(iterator.hasNext())
            {
                BooleanClause next = iterator.next();
                next.getQuery().setBoost(next.getQuery().getBoost() * idfBoost);
            }
            return bq;
        }

    }