FieldCache在lucene中使用的代码解析,使用场景个人分析

http://moshalanye.iteye.com/blog/281379


这篇文章的由来是在寻求lucene的搜索的性能提高的过程中成形的, 
感谢所有所以给于我帮助的朋友,在baseworld的提示下,我仔细翻阅了代码, 
于是想把自己的一些收获和想法写出来,希望对在学习的人提供帮助, 
更希望有人不吝啬手中的砖头,指正我的想法 



FieldCache为FieldCacheImpl的接口, 
其中有个名为default类型为FieldCacheImpl的静态对象, 


FieldCacheImpl中包含一些重要的不同类型的Cache属性, 
例如:bytesCache,stringsCache。。。  
均继承于FieldCacheImpl.Cache抽象类,均实现抽象接口 
protected abstract Object createValue(IndexReader reader, Object key) 
        throws IOException; 
例如: 

Java代码    收藏代码
  1. Cache bytesCache = new Cache() {  
  2.   
  3.    protected Object createValue(IndexReader reader, Object entryKey)  
  4.        throws IOException {  
  5.      /*获得需要处理的域的信息*     /                                                                                  
  6.      Entry entry = (Entry) entryKey; 
  7.      /*获得需要处理的域名*/    
  8.      String field = entry.field;  
  9.      /*获得类型转换器,将String转成byte*/  
  10.      ByteParser parser = (ByteParser) entry.custom;  
  11.      /*声明缓存的数据,注意该数据声明的大小为reader。maxDoc(),即最大Doc*/  
  12.      final byte[] retArray = new byte[reader.maxDoc()];  
  13.      /*获得通过term查找包含其的doc号*/  
  14.      TermDocs termDocs = reader.termDocs();  
  15.      /*term的循环器,参数为new Term (field, ""),即从该域开始循环*/  
  16.      TermEnum termEnum = reader.terms (new Term (field, ""));  
  17.      try {  
  18.        do {  
  19.          /*开始loop*/  
  20.          Term term = termEnum.term();  
  21.          if (term==null || term.field() != field) break;  
  22.          /*将term中的text转换成byte*/  
  23.          byte termval = parser.parseByte(term.text());  
  24.          /*寻找包含该term的doc*/  
  25.          termDocs.seek (termEnum);  
  26.          while (termDocs.next()) {  
  27.   
  28.     /* 
  29.        并把retArray数组该doc号index下的值赋值。 
  30.        在此处注意!!如果一个doc包含多个term, 
  31.        那么意味着该retArray[termDocs.doc()]会被覆盖, 
  32.        值将是最后次赋的值,也就是说cache的数据是限制了doc包含的数据个数据的, 
  33.        doc包含1个以上,cache便起不到真正的效果了,所以使用该cache的场景有限, 
  34.        我同样也考虑过改写cache,让其支持doc包含多个数据的情况, 
  35.        但突然发现这个跟把Index的数据全缓存在内存中没有什么区别! 矛盾啊! 
  36.        可能我的思路 还是受了限制阿!希望有人能给个思路 :D  
  37.     */  
  38.            retArray[termDocs.doc()] = termval;  
  39.          }  
  40.        } while (termEnum.next());  
  41.      } finally {  
  42.        termDocs.close();  
  43.        termEnum.close();  
  44.      }  
  45.        /*返回缓存的数据*/  
  46.      return retArray;  
  47.    }  
  48.  };  
  49.   
  50.   
  51.    static class Entry {  
  52.    final String field;        /*which Fieldable  域名*/  
  53.    final int type;            /*which SortField type 用于转换的的类型*/  
  54.    final Object custom;       /* which custom comparator      转换器*/  
  55.    final Locale locale;       /* 
  56.                                 the locale we're sorting (if string) 
  57.                                 国际化的咚咚 
  58.                               */  


以下是Cache的get的方法 
Cache的get方法 

Java代码    收藏代码
  1. public Object get(IndexReader reader, Object key) throws IOException {  
  2.   /*零时变量,存放该reader对应的缓存数据*/  
  3.   Map innerCache;  
  4.   Object value;  
  5.   /*readerCache缓存不通的reader读取的缓存数据*/  
  6.   synchronized (readerCache) {  
  7.   /*获取该reader对应的缓存数据*/   
  8.     innerCache = (Map) readerCache.get(reader);  
  9.     if (innerCache == null) {  
  10.     /*当没有该reader的缓存数据时,声明一个HashMap来存放该reader的缓存数据*/   
  11.       innerCache = new HashMap();  
  12.       /*放入到readerCache中,此时Map中没有数据*/  
  13.       readerCache.put(reader, innerCache);  
  14.       /*返回数据为null*/  
  15.       value = null;  
  16.     } else {  
  17.       value = innerCache.get(key);  
  18.     }  
  19.     if (value == null) {  
  20.       /*创建value的容器*/  
  21.       value = new CreationPlaceholder();  
  22.       /*并将容器放入对应的地方*/  
  23.       innerCache.put(key, value);  
  24.     }  
  25.   }  
  26.   if (value instanceof CreationPlaceholder) {  
  27.      /* 
  28.       对容器进行synchronized!该处是一个小技巧,在同步缓存数据时, 
  29.       有可能该值为null,对null不能同步,于是包了个马甲,马甲不为Null, 
  30.       马甲中的值为null 
  31.      */  
  32.      synchronized (value) {  
  33.       CreationPlaceholder progress = (CreationPlaceholder) value;  
  34.       if (progress.value == null) {  
  35.         /*当容器中缓存数据不存在!开始缓存!*/  
  36.         progress.value = createValue(reader, key);  
  37.         /* 
  38.          修改马甲,把马甲脱掉!由于修改了readerCache所以对它同步!  
  39.          我也有个疑惑?是否可以只对innerCache就可以了 
  40.         */  
  41.         synchronized (readerCache) {  
  42.           innerCache.put(key, progress.value);  
  43.         }  
  44.       }  
  45.       return progress.value;  
  46.     }  
  47.   }  
  48.   return value;  
  49. }  



ExtendedFieldCacheImpl 继承也只是扩展了以下 
class ExtendedFieldCacheImpl extends FieldCacheImpl implements ExtendedFieldCache 




下面介绍下FieldCache的应用场景,首先撤下题外话引一下 


HitQueue extends 优先权队列(优先队列就不介绍了,有时间可以看看代码) 
Java代码    收藏代码
  1. final class HitQueue extends PriorityQueue {  
  2.   /*初始化HitQueue时,初始化队列大小*/  
  3.   HitQueue(int size) {  
  4.     initialize(size);  
  5.   }  
  6.   /*重点方法,优先级别判断, ## doc的排序由它而来 ##*/  
  7.   protected final boolean lessThan(Object a, Object b) {  
  8.     ScoreDoc hitA = (ScoreDoc)a;  
  9.     ScoreDoc hitB = (ScoreDoc)b;  
  10.     /*通过得分来判断,得分高的排前*/  
  11.     if (hitA.score == hitB.score)  
  12.       return hitA.doc > hitB.doc;   
  13.     else  
  14.       return hitA.score < hitB.score;  
  15.   }  
  16. }  
  17.   
  18.   
  19. /* 
  20.    collector负责收集结果集,继承抽象类HitCollector, 
  21.    包含重要内部对象PriorityQueue hq 
  22. */  
  23. public class TopDocCollector extends HitCollector {  
  24.   /*零时变量,用以申明零时ScoreDoc的引用*/   
  25.   private ScoreDoc reusableSD;  
  26.   
  27.   int totalHits;  
  28.   PriorityQueue hq;  
  29.       
  30.   /** Construct to collect a given number of hits. 
  31.    * @param numHits the maximum number of hits to collect 
  32.    */  
  33.   /*默认构造HitQueue,即靠doc得分进行排序的优先队列*/  
  34.   public TopDocCollector(int numHits) {  
  35.     this(numHits, new HitQueue(numHits));  
  36.   }  
  37.   
  38. /*重点方法,获取topDocs*/  
  39.  public TopDocs topDocs() {  
  40.     ScoreDoc[] scoreDocs = new ScoreDoc[hq.size()];  
  41.     /*loop-- 从优先队列中获取数据*/  
  42.     for (int i = hq.size()-1; i >= 0; i--)      // put docs in array  
  43.       scoreDocs[i] = (ScoreDoc)hq.pop();  
  44.         
  45.     float maxScore = (totalHits==0)  
  46.       ? Float.NEGATIVE_INFINITY  
  47.       : scoreDocs[0].score;  
  48.     /*返回TopDocs对象*/      
  49.     return new TopDocs(totalHits, scoreDocs, maxScore);  
  50.   }  


开始进入正题吧! 

FieldSortedHitQueue同样继承 优先队列 

Java代码    收藏代码
  1. public class FieldSortedHitQueue  
  2. extends PriorityQueue {  
  3.   
  4.   /* 
  5.    参数reader作为该queue的参数,在这里面通过reader来获得比较器。 
  6.    参数fields作为排序的参数,排序的域 
  7.    参数size还是老规矩麻,来指定queue的大小  
  8.   */   
  9.   public FieldSortedHitQueue (   
  10.                  IndexReader reader,   
  11.                  SortField[] fields,  
  12.                  int size  
  13.                              )  
  14.   throws IOException {  
  15.     final int n = fields.length;  
  16.     /*有几个比较的域,则申明几个比较器*/  
  17.     comparators = new ScoreDocComparator[n];  
  18.     this.fields = new SortField[n];  
  19.     /*循环获得比较器,并重新初始化this.fields*/  
  20.     for (int i=0; i<n; ++i) {  
  21.       String fieldname = fields[i].getField();  
  22.       comparators[i] = getCachedComparator (  
  23.                                reader,   
  24.                                fieldname,   
  25.                                fields[i].getType(),   
  26.                                fields[i].getLocale(),   
  27.                                fields[i].getFactory()  
  28.                                            );  
  29.         
  30.       if (comparators[i].sortType() == SortField.STRING) {  
  31.           this.fields[i] = new SortField (  
  32.             fieldname, fields[i].getLocale(), fields[i].getReverse()  
  33.                                         );  
  34.       } else {  
  35.           this.fields[i] = new SortField (  
  36.             fieldname, comparators[i].sortType(), fields[i].getReverse()  
  37.                                         );  
  38.       }  
  39.     }  
  40.     /*初始化queue大小*/  
  41.     initialize (size);  
  42.   }  




在此介绍比较重要的方法,也是为什么会用到FieldCache的方法该方法用以获得比较器。 
还记得FieldCacheImpl的那个件马甲中的东西是什么吗?如果不记得,在这里先提醒下, 
那个是一个maxDoc长度的数组,数组的类型看你要的东西是什么类型的 
Java代码    收藏代码
  1. static ScoreDocComparator getCachedComparator (  
  2.          IndexReader reader,   
  3.          String field,   
  4.          int type,   
  5.          Locale locale,   
  6.          SortComparatorSource factory  
  7.   )  
  8.   throws IOException {  
  9.     /*当需要的是doc的比较类型,则给的是默认的coreDocComparator.INDEXORDER*/  
  10.     if (type == SortField.DOC) return ScoreDocComparator.INDEXORDER;  
  11.       
  12.     /*当需要的是score的比较类型,则给的是默认的coreDocComparator.RELEVANCE*/  
  13.     if (type == SortField.SCORE) return ScoreDocComparator.RELEVANCE;  
  14.       
  15.     /* 
  16.      初始化查缓存的参数,factory不为null,则用factory初始化, 
  17.      否则用type和locale来构造 
  18.     */   
  19.     FieldCacheImpl.Entry entry = (factory != null)  
  20.       ? new FieldCacheImpl.Entry (field, factory)  
  21.       : new FieldCacheImpl.Entry (field, type, locale);  
  22.   
  23.     /* 
  24.     此处的comparators是个什么列??赫赫! 按名字看貌似是一个装比较器的cache, 
  25.     进入看看他的做法,看它怎么cache比较器吧! 
  26.     */   
  27.     return (ScoreDocComparator)comparators.get(reader, entry);  
  28.   }  


又一个cache!装比较器的cache,我们来看看其中的代码, 
get方法都是cache抽象类中的,这里就不解释了 
Java代码    收藏代码
  1. static final FieldCacheImpl.Cache Comparators = new FieldCacheImpl.Cache() {  
  2.   
  3.   protected Object createValue(IndexReader reader, Object entryKey)  
  4.       throws IOException {  
  5.     /* 转参数entryKey; */  
  6.     FieldCacheImpl.Entry entry = (FieldCacheImpl.Entry) entryKey;  
  7.     /* 获得排序的field */  
  8.     String fieldname = entry.field;  
  9.     /* 获得排序的值类型,也可以说是将field的值转换后的类型 */  
  10.     int type = entry.type;  
  11.     /* 获取locale */  
  12.     Locale locale = entry.locale;  
  13.     /* 获取制造比较器的工厂,一般用于自定义比较器的生产 */  
  14.     SortComparatorSource factory = (SortComparatorSource) entry.custom;  
  15.     /* 返回的比较器 */  
  16.     ScoreDocComparator comparator;  
  17.     /* 根据type来获得比较器 */  
  18.     switch (type) {  
  19.       case SortField.AUTO:  
  20.         comparator = comparatorAuto (reader, fieldname);  
  21.         break;  
  22.       case SortField.INT:  
  23.         comparator = comparatorInt (reader, fieldname);  
  24.         break;  
  25.       case SortField.FLOAT:  
  26.         comparator = comparatorFloat (reader, fieldname);  
  27.         break;  
  28.       case SortField.LONG:  
  29.         comparator = comparatorLong(reader, fieldname);  
  30.         break;  
  31.       case SortField.DOUBLE:  
  32.         comparator = comparatorDouble(reader, fieldname);  
  33.         break;  
  34.       case SortField.STRING:  
  35.         if (locale != null) comparator =   
  36.               comparatorStringLocale (reader, fieldname, locale);  
  37.         else comparator =   
  38.               comparatorString (reader, fieldname);  
  39.         break;  
  40.       case SortField.CUSTOM:  
  41.         comparator = factory.newComparator (reader, fieldname);  
  42.         break;  
  43.       default:  
  44.         throw new RuntimeException ("unknown field type: "+type);  
  45.     }  
  46.     return comparator;  
  47.   }  
  48. };  



我取其中的两个比较有代表性的解释 
   
代表性一:    
       case SortField.INT: 
       comparator = comparatorInt (reader, fieldname); 

Java代码    收藏代码
  1. /* 该方法获得该field的int类型的比较器 */  
  2.   static ScoreDocComparator comparatorInt (  
  3.                         final IndexReader reader,   
  4.                         final String fieldname  
  5.                                            )  
  6.   throws IOException {  
  7.     /*  
  8.       获取域fieldname.intern()使用默认string池中的对象,使对象开销变少, 
  9.       鼓励这样的使用 
  10.     */       
  11.     final String field = fieldname.intern();  
  12.       
  13.     /*  
  14.       重点的重点!!!!千呼万唤始出来!在这里使用FieldCache.DEFAULT.getInts, 
  15.       获取缓存中数据(缓存数据数组的长度为maxDoc,index为doc号, 
  16.       值为该field的值转成了int) 
  17.     */   
  18.     final int[] fieldOrder = FieldCache.DEFAULT.getInts (reader, field);  
  19.       
  20.     /* OK!开始以缓存数据为基础编制马甲ScoreDocComparator */  
  21.     return new ScoreDocComparator() {  
  22.         
  23.       public final int compare (final ScoreDoc i, final ScoreDoc j) {  
  24.         final int fi = fieldOrder[i.doc];  
  25.         final int fj = fieldOrder[j.doc];  
  26.         if (fi < fj) return -1;  
  27.         if (fi > fj) return 1;  
  28.         return 0;  
  29.       }  
  30.   
  31.       public Comparable sortValue (final ScoreDoc i) {  
  32.         return new Integer (fieldOrder[i.doc]);  
  33.       }  
  34.   
  35.       public int sortType() {  
  36.         return SortField.INT;  
  37.       }  
  38.     };  
  39.   }  


代表性二: 

  case SortField.CUSTOM: 
     comparator = factory.newComparator (reader, fieldname); 

使用工厂来factory.newComparator 制造compatator 

如果有人写过自定义排序,肯定会接触SortComparator, 
而该类继承SortComparatorSource,即compatator得工厂, 
让我们看看他的实现吧  

Java代码    收藏代码
  1. /*  
  2.    该类是ScoreDocComparator的工厂类,然而又不是纯粹的工厂类, 
  3.    加了一个转换数据的功能,所以名字有点四不像了 
  4.  */  
  5. public abstract class SortComparator  
  6. implements SortComparatorSource {  
  7.   
  8.   // inherit javadocs  
  9.   public ScoreDocComparator newComparator (  
  10.                 final IndexReader reader,   
  11.                 final String fieldname  
  12.                                           )  
  13.   throws IOException {  
  14.     final String field = fieldname.intern();  
  15.     /*  
  16.        获取可比较的类的对象数组,Integer Long  都是可比较的, 
  17.        继承了comparable接口,这里的方法 FieldCache.DEFAULT. 
  18.        getCustom (reader, field, SortComparator.this)  
  19.        参数三是将本类的调用对象传给了FieldCache.DEFAULT.getCustom()方法, 
  20.        目的没别的!就是回调该 Comparable getComparable (String termtext)方法; 
  21.     */   
  22.     final Comparable[] cachedValues =   
  23.            FieldCache.DEFAULT.getCustom (reader, field, SortComparator.this);  
  24.       
  25.     return new ScoreDocComparator() {  
  26.   
  27.       public int compare (ScoreDoc i, ScoreDoc j) {  
  28.         return cachedValues[i.doc].compareTo (cachedValues[j.doc]);  
  29.       }  
  30.   
  31.       public Comparable sortValue (ScoreDoc i) {  
  32.         return cachedValues[i.doc];  
  33.       }  
  34.   
  35.       public int sortType(){  
  36.         return SortField.CUSTOM;  
  37.       }  
  38.     };  
  39.   }  
  40.   
  41.   /** 
  42.      抽象方法,用于把termtext转换成可比较的数据,即继承过Comparable的对象, 
  43.      在factory中多了一个工具功能 
  44.    */  
  45.   protected abstract Comparable getComparable (String termtext);  
  46.   
  47. }  


那么我们来顺藤摸瓜吧,找到了FieldCache.DEFAULT.getCustom (reader, field, SortComparator.this)方法调用了customCache,之前介绍过其他的cache(ByteCache), 
但没有介绍customCache,其实他们的区别就是获取缓存的数据时, 
将term的数据转换成custom想要的东西,即客户自定义!从而支持扩展 
Java代码    收藏代码
  1. Cache customCache = new Cache() {  
  2.   
  3.   protected Object createValue(IndexReader reader, Object entryKey)  
  4.       throws IOException {  
  5.     Entry entry = (Entry) entryKey;  
  6.     String field = entry.field;  
  7.     /*   
  8.        获取传过来的SortComparator对象 
  9.       (批评一下,名字取得够烂!纯粹迷幻名称! :shock:  
  10.        又是工厂又是获取可比较值的工具转换类叫这个名字,把人晕的不轻, 
  11.        害我多读好多遍代码) 
  12.     */  
  13.     SortComparator comparator = (SortComparator) entry.custom;  
  14.     final Comparable[] retArray = new Comparable[reader.maxDoc()];  
  15.     TermDocs termDocs = reader.termDocs();  
  16.     TermEnum termEnum = reader.terms (new Term (field, ""));  
  17.     try {  
  18.       do {  
  19.         Term term = termEnum.term();  
  20.         if (term==null || term.field() != field) break;  
  21.         /* 回调getComparable方法!获取可比较的值,其他的就不细说了!  */  
  22.         Comparable termval = comparator.getComparable (term.text());  
  23.         termDocs.seek (termEnum);  
  24.         while (termDocs.next()) {  
  25.           retArray[termDocs.doc()] = termval;  
  26.         }  
  27.       } while (termEnum.next());  
  28.     } finally {  
  29.       termDocs.close();  
  30.       termEnum.close();  
  31.     }  
  32.     return retArray;  
  33.   }  
  34. };  
  35.   
  36.   
  37.   
  38.    
  39.   sortField该构造函数支持自定义排序的,  
  40.   SortComparatorSource 即自定义排序的工厂类,   
  41.   一般都是继承SortComparatorSource 的子类 SortComparator。  
  42.   
  43. public SortField (String field, SortComparatorSource comparator, boolean reverse) {  
  44.   this.field = (field != null) ? field.intern() : field;  
  45.   this.type = CUSTOM;  
  46.   this.reverse = reverse;  
  47.   this.factory = comparator;  
  48. }  



接着来介绍fieldcache的比较适用的场景吧! 
第一个肯定是自定义排序了,但这个无需我们去使用,代码已经帮我们封装过了 


第二个比较自由的就是filter了,面对doc的中存储某种type的域的情况, 
通过type过滤时,这个时候就可以适当选择cache了 

这个是我写的一个数字的filter,因为浮点数包括了整数,写了一个数字的filter  
Java代码    收藏代码
  1. public class DigitalFilter extends Filter {  
  2.     static final String digitalRegex = "^\\d*\\.?\\d*$";  
  3.   
  4.     private Term t;  
  5.   
  6.     public DigitalFilter(Term t) {  
  7.         this.t = t;  
  8.     }  
  9.   
  10.     public BitSet bits(IndexReader reader) throws IOException {  
  11.         BitSet bitSet = new BitSet(reader.maxDoc());  
  12.         /* 在此处调用了cache的getFloats方法,ExtendedFieldCache只是扩展了FieldCache,增加了LongCache 和DoubleCache,其他一样 */   
  13.         float digital[] = ExtendedFieldCache.DEFAULT.getFloats(  
  14.                 reader,  
  15.                 t.field(),  
  16.                 new FieldCache.FloatParser() {  
  17.                     public float parseFloat(String string) {  
  18.                         if (string.matches(digitalRegex))  
  19.                             return Float.parseFloat(string);  
  20.                         return 0;  
  21.                     }  
  22.                 });  
  23.         for (int i = 0; i < digital.length; i++) {  
  24.             if (digital[i] == Float.parseFloat(t.text()))  
  25.                 bitSet.set(i);  
  26.         }  
  27.         return bitSet;  
  28.     }  
  29.   
  30.     public static void main(String args[]) {  
  31.     }  
  32. }  




解析就到这里了!代码整理是由baseworld的提点,导致了这些的产生, 
在此感谢所有给我建议的人,感谢你们的帮助, 
如果有人在fieldcache有不错的用法,请不吝啬手上的砖头,给点启发! 
谢谢!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值