`
fwuwen
  • 浏览: 16258 次
  • 来自: 厦门
文章分类
社区版块
存档分类
最新评论

solr4.2 edismax查询方式评分计算

 
阅读更多

      lucene从4.0开始就提供了多个打分模型,包括TF-IDF,BM25,DRF等。默认的实现还是基于经典的TFIDF模型。下面对solr edismax查询中涉及到的一些公式进行介绍。

 

tf(float freq):词频,freq为某个词在该文档的相应field中出现的次数, 默认为Math.sqrt(freq):

idf(long docFreq, long numDocs):逆文档频数,docFreq为term总共在几个文档中出现,numDocs为文档总数.默认为(Math.log(numDocs/(double)(docFreq+1)) + 1.0)

queryNorm(float sumOfSquaredWeights):不影响排序,每个命中的文档都会乘以该因子。仅仅使得不同的query之间的分数可以比较。在不同的查询条件,或者索引不同的情况下(比如分布式查询),需要考虑到该因素的影响。默认为(float)(1.0 / Math.sqrt(sumOfSquaredWeights))

lengthNorm():创建索引时候写入nrm文件中,默认是1.0 / Math.sqrt(numTerms) numTerms为添加的field中term的总数,采用1个byte保存

queryBoost:查询设置的权重

 

lucene中可以在建立索引时候添加权重,也可以通过查询时动态设置权重。建立索引的时候可以通过设置

Field.setBoost: 字段权重

Document.setBoost: 文档权重,在lucene4中已经去掉.solr4中保留了此参数.实际上也是通过设置Field.setBoost来完成,在4中如果同时设置了Field.setBoost以及Document.setBoost,则最后该字段的权重为Field.setBoost*Document.setBoost.相关代码可以查看solr中DocumentBuilder.toDocument.

 

通过field.setboost设置的权重,主要是通过lengthNorm()来实现。

 

 public float lengthNorm(FieldInvertState state) {
    final int numTerms;	        // 分词出来的term数量,state.getBoost() 就是建立索引时候设置的权重
    if (discountOverlaps)	// 是否忽略指定的term数量
      numTerms = state.getLength() - state.getNumOverlap();
    else
      numTerms = state.getLength();
   return state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));
  }

lucene中采用1个byte来存储lengthNorm的值,默认是采用3个bit来区分这个值,最小单位为0.125.最多存储了256个不同的boost。当boost设置成20或者23时候,是没有区别的。所以会造成精度上的损失.

关于norm的精度计算方式可以查看smallfloat.

/** Cache of decoded bytes. */
  private static final float[] NORM_TABLE = new float[256];

  static {
    for (int i = 0; i < 256; i++) {
      NORM_TABLE[i] = SmallFloat.byte315ToFloat((byte)i);
    }
  }

 

edismax中打分的主要流程:

 

search(leafContexts, createNormalizedWeight(wrapFilter(query, filter)), results)

     createNormalizedWeight()

           DisjunctionMaxWeight.score()

                  DisjunctionMaxScorer.score() ---- 返回最终的分数

 

createNormalizedWeight:

 

public Weight createNormalizedWeight(Query query) throws IOException {
    query = rewrite(query);			// DisjunctionMaxQuery
    Weight weight = query.createWeight(this);	// DisjunctionMaxWeight	计算idf,各个子weight的queryWeight
    float v = weight.getValueForNormalization();// 返回queryNorm中的参数
    float norm = getSimilarity().queryNorm(v);	// 计算queryNorm
    if (Float.isInfinite(norm) || Float.isNaN(norm)) {
      norm = 1.0f;
    }
    weight.normalize(norm, 1.0f);		//  计算各个子weight的weightValue
    return weight;
  }
 getValueForNormalization:

 

/**
     * queryNorm(float sumOfSquaredWeights)计算
     * 
     * @return
     * 		sumOfSquaredWeights的值
     */
    public float getValueForNormalization() throws IOException {
      /**
       * sum 各个weight的总得分, max 所有weight中最大的一个
       */
      float max = 0.0f, sum = 0.0f;		
      for (Weight currentWeight : weights) {
        float sub = currentWeight.getValueForNormalization();	// sub=queryWeight * queryWeight
        sum += sub;
        max = Math.max(max, sub);
      }
      float boost = getBoost();		// 1.0f DisjunctionMaxQuery中没有设置boost,只是在子query中进行了设置
      return (((sum - max) * tieBreakerMultiplier * tieBreakerMultiplier) + max) * boost * boost;	// sumOfSquaredWeights计算公式
    }

 DisjunctionMaxWeight.score()

 

 public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
        boolean topScorer, Bits acceptDocs) throws IOException {
      Scorer[] scorers = new Scorer[weights.size()];
      int idx = 0;
      for (Weight w : weights) {
        // we will advance() subscorers
        Scorer subScorer = w.scorer(context, true, false, acceptDocs);	// 返回每个weight的Scorer对象
        if (subScorer != null) {
          scorers[idx++] = subScorer;		// 放入Scorer数组中
        }
      }
      if (idx == 0) return null; // all scorers did not have documents
      DisjunctionMaxScorer result = new DisjunctionMaxScorer(this, tieBreakerMultiplier, scorers, idx);
      return result;	// 返回DisjunctionMaxScorer对象
    }
 DisjunctionMaxScorer.score()
public float score() throws IOException {
    int doc = subScorers[0].docID();						// 获取文档号
    scoreSum = scoreMax = subScorers[0].score(); // 获取第一个weight的评分
    int size = numScorers; // 需要计算的score总数 
    /**
     * 遍历subScorers[] 计算scoreSum和scoreMax
     * scoreMax为每个subScorers中分数最大的一个
     */
    scoreAll(1, size, doc);
    scoreAll(2, size, doc);
    /**
     * 返回最终的分数
     * 
     * tieBreakerMultiplier: 默认为0,solr中可以通过tie参数来设置
     * 假设有两个字段content,title. q参数同时在content,title中命中,最后得分如下
     * 		content  title  scoreMax   scoreSum
     * doc1:    0.1	 0.5	0.5	       0.6	
     * doc2:    0.5	 0.3	0.5	       0.8
     * 
     * doc1和doc2在默认的情况下得分是相同的,实际的情况是我们希望doc2获取更高的分数,
     * 这时可以通过设置tieBreakerMultiplier为0.1,来使得doc2获取更高的分数,最后新的score为:
     * score(doc1) = 0.51
     * score(doc2) = 0.53
     */
    return scoreMax + (scoreSum - scoreMax) * tieBreakerMultiplier;
  }
 DisjunctionMaxScorer.scoreAll
 private void scoreAll(int root, int size, int doc) throws IOException {
    if (root < size && subScorers[root].docID() == doc) {
      float sub = subScorers[root].score();		// 获取tf的分数,在freq相同的情况下还有两个影响的分数就是norms.get[doc],weightValue
      scoreSum += sub;
      scoreMax = Math.max(scoreMax, sub);    // scoreMax为每个评分器中最大的一个
      scoreAll((root<<1)+1, size, doc);
      scoreAll((root<<1)+2, size, doc);
    }
  }
 最终的tf-idf分数计算,ExactTFIDFDocScorer.score
 @Override
    public float score(int doc, int freq) {
      final float raw = tf(freq)*weightValue;  // compute tf(f)*weight
      // decodeNormValue((byte)norms.get(doc) 即为lengthNorm的值
      return norms == null ? raw : raw * decodeNormValue((byte)norms.get(doc)); // normalize for field
    }
 
edismax查询分数计算的伪代码:
  for (Weight currentWeight : weights) {
        queryWeight = idf * queryBoost		// normalize之前
        float sub = queryWeight * queryWeight 
        sum += sub;
        max = Math.max(max, sub);
  }
  sumOfSquaredWeights = (((sum - max) * tieBreakerMultiplier * tieBreakerMultiplier) + max) * 1.0 * 1.0;
  queryNorm = (float)(1.0 / Math.sqrt(sumOfSquaredWeights))

 for(Score subScore : Scores) {
         queryWeight = idf * queryBoost * queryNorm() * 1.0   // normalize之后
         weightValue = queryWeight * idf
         if (omitNorms) {
             subscore = tf(freq)*weightValue
         } else {
             subscore = tf(freq)*weightValue *lengthNorm
         }
             scoreSum += subscore;
         scoreMax = Math.max(scoreMax, subscore);
 }

 最后的分数为: scoreMax + (scoreSum - scoreMax) * tieBreakerMultiplier
 
分享到:
评论

相关推荐

    ik分词包 用于lucene4.2和solr4.2的最新包

    自从lucene和solr推出4.0版本后 ik分词的调用接口已经不适用了,虽说ik最新ff版适用于solr4.0 但是solr4.2出来之后发现又不适用了,本人花了一点功夫熟悉solr4.2的分词调用接口补写了一个IkTokenizerFactory类 经...

    maven 搭建solr4.2源码环境

    maven 整合solr4.2环境,另外整合了solr-data-import源码环境,资源10分,十分不贵!有需要的朋友请下载吧。花了我3个小时的时间整理的。下载后,使用maven导入即可使用,升级solr版本也比较方便。本环境使用了...

    Solr-4.2-api-docs.chm

    这是最新的全文检索引擎Solr4.2 Solrj API 包含全部的api 其中还有部分官方文档,

    solr-4.2.0

    5. **请求处理器与查询解析器**:Solr提供了丰富的请求处理器和查询解析器,4.2.0版本可能包含了新的或优化的解析器,如DisMax和EDismax,它们能够更好地处理用户输入,提高搜索体验。 6. **XML和JSON API增强**:...

    solr实现电商自定义打分

    Solr的默认评分是基于TF-IDF(词频-逆文档频率)算法的,它会根据查询词在文档中的出现频率和在整个索引中的普遍程度来计算相关性。然而,这种默认设置可能并不完全符合电商场景的需求,例如,我们可能希望销量高、...

    基于Solr的多表join查询加速方法

    - **数据预处理**:在索引阶段,预先计算join结果并存储在Solr文档中,查询时直接读取,避免运行时的join操作。 - **查询设计**:通过调整查询语句的结构,如使用“exists”查询或“join”查询,以适应Solr的查询...

    Solr评分整理汇总.docx

    Solr 评分整理汇总 Solr 评分整理汇总是指 Solr 中的评分机制,该机制是继承自 Lucene 的文本...Solr 评分整理汇总是指 Solr 中的评分机制,该机制可以根据实际业务需求定制自己的一套打分算法来获取理想的查询结果。

    Solr 查询,索引

    3. **查询优化**:Solr会根据查询条件和索引结构自动优化查询计划,包括使用倒排索引来快速定位匹配文档,以及对查询结果进行评分排序。 4. **高亮显示**:Solr能够对查询结果中的匹配词进行高亮,提高用户查找相关...

    solr源码及文档

    solr全文检索,里面包含文档,源代码,jar包,使用的是solr4.2,东西比较全,安装文档就能跑起来,,适合参考借鉴

    solr-api-4.2

    solr-api-4.2

    solr自定义评分组件demo.zip

    例如,可以基于文档的某些字段值、时间戳或者其他业务指标来计算评分。此外,还需要提供对应的解析器(Parser)以处理查询中的函数表达式。 自定义评分组件的流程大致如下: 1. **定义评分逻辑**:在Java类中实现...

    solr-6.2.0源码

    Solr支持多种数据存储方式,如内存存储和硬盘存储,以及分布式索引和查询处理,使得它可以轻松应对大数据量的场景。 二、Solr的特性 1. 分布式搜索:Solr 6.2.0支持集群部署,可以将索引分片到多个节点,实现水平...

    图解solr5.0.1 war包方式安装【原创】

    ### 图解Solr 5.0.1 WAR包方式安装详解 #### 一、Solr简介及WAR包安装概述 Solr是一款开源的全文搜索引擎平台,由Apache软件基金会开发维护,广泛应用于各种需要进行高性能搜索的应用场景。Solr支持多种语言、协议...

    图解Solr5.3.1 war包方式安装

    ### 图解Solr5.3.1 war包方式安装 #### 一、Solr简介 Solr是一款开源的全文搜索引擎平台,基于Java开发,能够提供高效、稳定的搜索服务。Solr利用Lucene作为其核心搜索库,同时提供了更加丰富的功能集,支持分布式...

    最新springboot solr查询

    Solr是一个开源搜索平台,用于构建搜索应用程序。Solr可以和Hadoop一起使用。由于Hadoop处理大量数据,Solr帮助我们从这么大的源中找到所需的信息。不仅限于搜索,Solr也可以用于存储目的。像其他NoSQL数据库一样,...

    solr查询语法.pdf

    Solr是一种基于Apache Lucene的开源搜索引擎,提供了丰富的查询语法来满足各种搜索需求。在了解Solr查询语法前,我们首先需要了解几个核心概念。 首先,Solr的查询解析是通过queryParser来配置的,通常使用默认配置...

    Linux上Solr的启动方式

    使用Solr内置的Jetty服务器启动Solr (1)借助X Shell上传solr的安装包到/usr/local/目录下,使用 tar -zxvf命令进行解压.  (2)使用内置的Jetty来启动Solr服务器只需要在example目录下,执行start.jar程序即可,...

    solr创建索引并查询

    ### Solr创建索引并查询的关键知识点 #### 一、Solr简介 Apache Solr是一款开源的全文搜索引擎平台,基于Lucene实现。它提供了一套完整的搜索解决方案,并且支持多种高级特性,如高亮显示、分面搜索、地理位置搜索...

Global site tag (gtag.js) - Google Analytics