Solr&Lucene --- 排序

x33g5p2x  于2021-12-20 转载在 其他  
字(2.7k)|赞(0)|评价(0)|浏览(313)

出处: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());
    }    
  }

相关文章