天天看點

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 != ) {
           return c > ;
      }
       // 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 = ; i < numComparators; ++i) {
        final int c = reverseMul[i] * comparators[i].compare(hitA.slot, hitB.slot);
        if (c != ) {
          // Short circuit
          return c > ;
        }
      }
      // 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 = ; 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 = ; 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());
    }    
  }
           

繼續閱讀