Solr&Lucene --- 排序

Posted Nireus_LOVE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Solr&Lucene --- 排序相关的知识,希望对你有一定的参考价值。

出处:http://ronxin999.blog.163.com/blog/static/42217920201110532554485/

luence 和solr排序都有排序功能,solr的排序就是基于luence的排序来实现的。solr通过url里加solr=true来排序,把后面带的参数封装成SortField,然后根据luence的底层来排序。下面开始讲luence排序的实现。

luence排序是基于luence有一个最小堆PriorityQueue,PriorityQueue最小堆的比较规则,由子类实现,即lessThan方法。
Luence的FieldValueHitQueue继承了PriorityQueue,我们排序的时候,有可能是根据一个field或者是多个field来排序。
那luence对应的FieldValueHitQueue有两个子类,分别是OneComparatorFieldValueHitQueue和MultiComparatorsFieldValueHitQueue,就是如果按一个Field排序和多个Field的比较方法不一样。分别如下:

//一个field排序的比较方法。
@Override
protected boolean lessThan(final Entry hitA, final Entry hitB) 
      assert hitA != hitB;
      assert hitA.slot != hitB.slot;
      final int c = oneReverseMul * comparator.compare(hitA.slot, hitB.slot);
      if (c != 0) 
           return c > 0;
      
       // avoid random sort order that could lead to duplicates (bug #31241):
      return hitA.doc > hitB.doc;
 

  //多个field排序的比较方法。
@Override
 protected boolean lessThan(final Entry hitA, final Entry hitB) 
      assert hitA != hitB;
      assert hitA.slot != hitB.slot;
      int numComparators = comparators.length;
      for (int i = 0; i < numComparators; ++i) 
        final int c = reverseMul[i] * comparators[i].compare(hitA.slot, hitB.slot);
        if (c != 0) 
          // Short circuit
          return c > 0;
        
      
      // avoid random sort order that could lead to duplicates (bug #31241):
      return hitA.doc > hitB.doc;

先讲下comparators和reverseMul:
comparators: 是一个数组,如果是当个Field,就是给comparators[0] = field.getComparator(size, 0);即根据不同Field不同的数据类型创建不同的比较器。如果是多个Field,则为每个Field创建一个比较器。
reverseMul:决定按升序还是降序。
从上面两个方法可以看出,如果是多个Field排序,如果第一个Field比较的结果不相等,则按第一Field决定,不会再比较后面的Field,如果第一个Field的值相等,则按后面的Field比较。如果都相等,则按docID的大小来比较。

比较器比较的肯定是要排序Field的值,那Field的值是在什么时候取到的呢,这就是比较器FieldComparator有一个setNextReader的方法。这个方法在Iuence的IndexSearch的search方法里回调用。代码如下:

//这里说明下,collector,如果有排序,collector为TopFieldCollector。
for (int i = 0; i < subReaders.length; i++)  // search each subreader
        collector.setNextReader(subReaders[i], docStarts[i]);
        Scorer scorer = weight.scorer(subReaders[i], !collector.acceptsDocsOutOfOrder(), true);
        if (scorer != null) 
          scorer.score(collector);
        
 
而TopFieldCollector的内部子类多个Field的排序的代码为:
@Override
public void setNextReader(IndexReader reader, int docBase) throws IOException 
      this.docBase = docBase;
      for (int i = 0; i < comparators.length; i++) 
        comparators[i].setNextReader(reader, docBase);
      
 

从上面可以看出,其实是比较器FieldComparator的setNextReader方法。FieldComparator的方法就是通过FieldCache的实现类FieldCacheImpl去取对应Field的值,如果没有,则通过Reader去索引库取,然后放到FieldCache缓存。

根据Solr源码发现,solr对排序段Field是有要求的,主要有两点:

1 field必须是索引的field。
2 field不能是multivalued 多个值的。

代码如下:

Solr在获取排序field时,会调用SchemaField的这个方法:
public void checkSortability() throws SolrException 
    if (! indexed() ) 
      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, 
                              "can not sort on unindexed field: " 
                              + getName());
    
    if ( multiValued() ) 
      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, 
                              "can not sort on multivalued field: " 
                              + getName());
        
  

以上是关于Solr&Lucene --- 排序的主要内容,如果未能解决你的问题,请参考以下文章

lucene入门&Solr

lucene&solr全文检索_7solr后台界面的介绍

Web-第二十九天 Lucene&solr使用二悟空教程

全文检索(Lucene&Solr)

lucene&solr全文检索_5索引的维护

lucene&solr全文检索_3查询索引