Solr&Lucene --- 排序

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

出处: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的比较方法不一样。分别如下:

  1. //一个field排序的比较方法。
  2. @Override
  3. protected boolean lessThan(final Entry hitA, final Entry hitB) {
  4. assert hitA != hitB;
  5. assert hitA.slot != hitB.slot;
  6. final int c = oneReverseMul * comparator.compare(hitA.slot, hitB.slot);
  7. if (c != 0) {
  8. return c > 0;
  9. }
  10. // avoid random sort order that could lead to duplicates (bug #31241):
  11. return hitA.doc > hitB.doc;
  12. }
  13. //多个field排序的比较方法。
  14. @Override
  15. protected boolean lessThan(final Entry hitA, final Entry hitB) {
  16. assert hitA != hitB;
  17. assert hitA.slot != hitB.slot;
  18. int numComparators = comparators.length;
  19. for (int i = 0; i < numComparators; ++i) {
  20. final int c = reverseMul[i] * comparators[i].compare(hitA.slot, hitB.slot);
  21. if (c != 0) {
  22. // Short circuit
  23. return c > 0;
  24. }
  25. }
  26. // avoid random sort order that could lead to duplicates (bug #31241):
  27. return hitA.doc > hitB.doc;
  28. }

先讲下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方法里回调用。代码如下:

  1. //这里说明下,collector,如果有排序,collector为TopFieldCollector。
  2. for (int i = 0; i < subReaders.length; i++) { // search each subreader
  3. collector.setNextReader(subReaders[i], docStarts[i]);
  4. Scorer scorer = weight.scorer(subReaders[i], !collector.acceptsDocsOutOfOrder(), true);
  5. if (scorer != null) {
  6. scorer.score(collector);
  7. }
  8. }
  9. TopFieldCollector的内部子类多个Field的排序的代码为:
  10. @Override
  11. public void setNextReader(IndexReader reader, int docBase) throws IOException {
  12. this.docBase = docBase;
  13. for (int i = 0; i < comparators.length; i++) {
  14. comparators[i].setNextReader(reader, docBase);
  15. }
  16. }

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

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

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

代码如下:

  1. Solr在获取排序field时,会调用SchemaField的这个方法:
  2. public void checkSortability() throws SolrException {
  3. if (! indexed() ) {
  4. throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
  5. "can not sort on unindexed field: "
  6. + getName());
  7. }
  8. if ( multiValued() ) {
  9. throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
  10. "can not sort on multivalued field: "
  11. + getName());
  12. }
  13. }

相关文章