Solr 函数查询(FunctionQuery)原理分析

来源:互联网 发布:中国人没有信仰 知乎 编辑:程序博客网 时间:2024/05/16 17:47

此文原创,转载请说明出处:http://ronxin999.blog.163.com/blog/static/4221792020111013131992/

solr 函数查询有4种方式来实现,这个在solr的wiki里描述的比较清楚,但是这个东西到底是个什么意思,原理是什么,solr wiki并没有说的很清楚,很多朋友也是对此一头雾水,现在我经过一定时间的调试,总结了点经验,写下来,和喜欢solr的朋友共勉。
这里说其中的一种,也是最诡异的一种实现方法:_val_
举个查询例子为q=title:hadoop _val_:count 这里顺便说下hadoop和_val_中间的空格,由于solr默认是OR,所以这里是一个or组合的booleanQuery,如果是q=title:hadoop  AND _val_:count 则是一个有AND组合的BooleanQuery查询。
title和count都是solr配置文件schema.xml里面的一个field,title:hadoop很明细,搜索文档的field值包含hadoop的文档。假如
索引库里总共有5个document 1,2,3,4,5,其中doucument 2,5的field包含hadoop值,那title:hadoop搜索到2,5两个document,

_val_:count 
由于_val_代表的是functionQuery,由于我们索引库里的每个document都包含count域。所以solr会把所有库里的5个document
ID取到,其实solr是直接读reader.maxDoc(),这里值为5,
由于是 OR,solr底层luence会对两者坐并集,然后对并集的doc ID 打分。关键就在这个打分,也是_val_:count的体现。
由于文档2,5在两个集合里都出现,所以,document 2和5的得分也是两个得分之和。title:hadoop返回的文档2和5的得分就是luence的评分公式计算出来的,而_val_:count是有FunctionQuery的内部类FunctionWeight来计算的,不过FunctionWeight计算权重时,idf的值为1,由FunctionWeight计算的值再乘以该document域count的值,所以整个的查询结果是5个document,但是2,5这个两个文档的得分会很高。但跟这个两个文档的count域的值有关。

同样如果是q=title:hadoop  AND _val_:count 逻辑于查询,那结果只有两个文档,因为luence会对其期交集,同样的是也会把相同的文档的得分相加。

顺便再说下,如果_val_:3这也,后面直接跟数据,solr就不要到索引库里去取指定域如上面count域的值了,而是直接为3,如果是跟field,solr会用到fieldCache,把该域的值取出来放到fieldCache中,下次取就不要再读索引库了。

这里再讲下_val_:rod(count)也就是_val_后面跟函数的情况。
这里用我自己自定义的函数代码如下:

public class MyValueSourceParser extends ValueSourceParser {

    public MyValueSourceParser(){
        super();
        System.out.println("========初始化MyValueSourceParser实例==========");
    }
    @Override
    public ValueSource parse(FunctionQParser fp) throws ParseException {
         System.out.println("========开始执行MyValueSourceParser的parse方法==========");
         String field = fp.parseId();
         //我们这里是要根据域field的值从而可以关联一个得分。我这个Field是字符串形式的。
         //StrKeywordsFieldSource  是继承StrFieldSource,solr有什么类型的Filed有相应的ValueSource。
         StrKeywordsFieldSource stringFieldSource = new StrKeywordsFieldSource(field);
         return stringFieldSource;

    }

}

//StrKeywordsFieldSource类的源码如下:
public class StrKeywordsFieldSource extends StrFieldSource {

    private static final long serialVersionUID = 1L;
    //必须的
    public StrKeywordsFieldSource(String field) {
        super(field);
        // TODO Auto-generated constructor stub
    }

   
    /**
     * 重写getValues方法,StrFieldSource就是取field对应的值,我们要 根据值再做具体的运算。
     * 由于,在函数查询里最终是调用getValues方法来取得结果的,所以我们都要重写getValues方法。
    * getValues方法返回的是DocValues ,计算得分是DocValues 的floatVal方法。我们的都要重写。
    */
    @Override
    public DocValues getValues(Map context, IndexReader reader) throws IOException {
        return new StringIndexDocValues(this, reader, field) {
          @Override
          protected String toTerm(String readableValue) {
            return readableValue;
          }

          @Override
          public float floatVal(int doc) {
              String isbn = strVal(doc);
              System.out.println("doc["+doc+"] isbn value is ["+isbn+"]");
              if("hadoop".equals(isbn)){
                  return 100f;
              }else if("action".equals(isbn)){
                  return 10f;
              }else{
                  return 1f;
              }
          }

          @Override
          public int intVal(int doc) {
            int ord=order[doc];
            return ord;
          }

          @Override
          public long longVal(int doc) {
            return (long)intVal(doc);
          }

          @Override
          public double doubleVal(int doc) {
            return (double)intVal(doc);
          }

          @Override
          public String strVal(int doc) {
            int ord=order[doc];
            return lookup[ord];
          }

          @Override
          public String toString(int doc) {
            return description() + '=' + strVal(doc);
          }
        };
      }

}

自己写的函数查询要添加到solrconfig.xml配置文件才能生效。配置文件修改如下:
//函数名为:testfunc
<valueSourceParser name="testfunc"
                        class="com.solr.parse.plugin.MyValueSourceParser" />

现在我们可以通过_val_:"testfunc(keywords)"来函数查询了。记住函数一定要用""引起来。
比如我查询标题有hadoop的文档为 q=title:hadoop AND _val_:"testfunc(keywords)"这样就查到包含hadoop的文档的keywords域的值可以改变打分。如果keywords域就一个值还,如果keywords域有多个term,那就有多个值。那用那个呢,还有如果文档比较少,但是keywords域包含的词有很多这个又是怎么弄的。经过测试。可以回答上面两个问题。
1 solr会把所有文档的keywords域的Term放在一个枚举里面,这所有的Term是按字典排好序的。也就是取的时候是按字典顺序   取的。假如我查询title域包含hadoop的文件就3个,而我这3个文档title域包含6个Term,那Solr只取按字典排好序的前3个Term.


讲了这么多,再总结下,其实solr函数查询关键取出所有的文档,在luence的打分基础上乘以函数计算的值作为得分。

0 0
原创粉丝点击