Lucene4.3进阶开发之柳暗花明( 六) - IndexSearcher



Lucene4.3进阶开发之柳暗花明( 六) - IndexSearcher
本篇文章散仙要介绍的是IndexSearcher这个类,这个类是Lucene在进行检索时必不可少的一个组件,可以称为是检索的入口,通过这个入口之后,我们就可以获取与我们检索的关键词相关的一系列Doc,然后我们就可以进行后续相关的业务处理。 
  1. Directory directory=FSDirectory.open(new File("D:\\索引测试"));//获取一个索引目录  
  2. IndexReader reader=DirectoryReader.open(directory);//返回一个复合Reader=》DirectoryReader  
  3.          //构造IndexSearcher 检索环境  
  4. IndexSearcher searcher=new IndexSearcher(reader);  

  1. final IndexReader reader; // package private for testing!  
  2.   // NOTE: these members might change in incompatible ways  
  3.   // in the next release  
  4.   protected final IndexReaderContext readerContext;  
  5.   protected final List<AtomicReaderContext> leafContexts;  
  6.   /** used with executor - each slice holds a set of leafs executed within one thread */  
  7.   protected final LeafSlice[] leafSlices;  
  8.   // These are only used for multi-threaded search  
  9.   private final ExecutorService executor;  
  10.   // the default Similarity  
  11.   private static final Similarity defaultSimilarity = new DefaultSimilarity();    
  12.   /** The Similarity implementation used by this searcher. */  
  13.   private Similarity similarity = defaultSimilarity;  
  14.   
  15.   /** Creates a searcher searching the provided index. */  
  16.   public IndexSearcher(IndexReader r) {  
  17.    //调用的是2参的构造函数  
  18.     this(r,null);  
  19.   }  
  20.   
  21.   /** Runs searches for each segment separately, using the 
  22.    *  provided ExecutorService.  IndexSearcher will not 
  23.    *  shutdown/awaitTermination this ExecutorService on 
  24.    *  close; you must do so, eventually, on your own.  NOTE: 
  25.    *  if you are using {@link NIOFSDirectory}, do not use 
  26.    *  the shutdownNow method of ExecutorService as this uses 
  27.    *  Thread.interrupt under-the-hood which can silently 
  28.    *  close file descriptors (see <a  
  29.    *  href="https://issues.apache.org/jira/browse/LUCENE-2239">LUCENE-2239</a>). 
  30.    *  
  31.    * @lucene.experimental */  
  32.   public IndexSearcher(IndexReader r, ExecutorService executor) {  
  33.        
  34.     this(r.getContext(), executor);  
  35.   }
看了源码,我们就会发现,我们常用的构造函数实际上是会调用含有线程池并行检索的2参的构造方法,只不过,把线程池设置为null而已,这其实是一个优化的操作,在某些时候能够带来极大的性能提升.

方法名描述
IndexSearcher(IndexReader r)构建一个搜索实例,使用指定的Reader
IndexSearcher(IndexReader r, ExecutorService executor)创建一个并行的检索实例,使用ExecutorService 提供的线程池
doc(int docID)通过一个docid获取一个对应的doc
explain(Query query, int doc)获取query详细的评分依据信息
getIndexReader()获取IndexReader实例
search(Query query, int n)获取前N个检索的结果
search(Query query, Collector results)通过collector对检索结果进行自定义控制
search(Query query, Filter filter, Collector results)通过检索,过滤,以及收集,获取一个特定的检索结果
search(Query query, Filter filter, int n)经过滤后 的前N个结果
search(Query query, Filter filter, int n, Sort sort)经过滤,排序后的前n个结果
search(Query query, Filter filter, int n, Sort sort, boolean doDocScores, boolean doMaxScore)对排序后的结果,是否开启评分策略
searchAfter(ScoreDoc after, Query query, int n)检索上一次query后的数据,通常用来分页使用
setSimilarity(Similarity similarity)设置自定义的打分策略
search(Weight weight, int nDocs, Sort sort, boolean doDocScores, boolean doMaxScore)检索指定分数以上的结果
IndexSearcher的并行构造,如何使用多线程来提升检索性能。

大多数时候,我们默认使用的都是单线程检索,这时候的检索总耗时是顺序检索所有段文件的时间之和,而如果我们使用了并行检索,这时候,我们的检索总耗时,其实就是检索段文件里,耗时最大的那个线程的时间,因为我们是并行检索,所以影响耗时的其实就是检索耗时最长的那个线程的耗时.


这个并行优化的功能,最适合的场景就是我的索引非常大,然后我们把这份索引,压缩成了多个段文件,可能有5个,或者10个以上的段文件,这时候利用这个功能,检索就有很大优势了.
  1. if (executor == null) {  
  2.       return search(leafContexts, weight, after, nDocs);  
  3.     } else {  
  4.         //通过一个公用的队列,来合并结果集  
  5.       final HitQueue hq = new HitQueue(nDocs, false);  
  6.       final Lock lock = new ReentrantLock();//锁  
  7.       final ExecutionHelper<TopDocs> runner = new ExecutionHelper<TopDocs>(executor);  
  8.       
  9.       for (int i = 0; i < leafSlices.length; i++) { // search each sub  
  10.         runner.submit(new SearcherCallableNoSort(lock, this, leafSlices[i], weight, after, nDocs, hq));  
  11.       }  
  12.   
  13.       int totalHits = 0;  
  14.       float maxScore = Float.NEGATIVE_INFINITY;  
  15.       for (final TopDocs topDocs : runner) {  
  16.         if(topDocs.totalHits != 0) {  
  17.           totalHits += topDocs.totalHits;  
  18.           maxScore = Math.max(maxScore, topDocs.getMaxScore());  
  19.         }  
  20.       }  
  21.   
  22.        //最后从队列里,取值给ScoreDoc进行返回  
  23.       final ScoreDoc[] scoreDocs = new ScoreDoc[hq.size()];  
  24.       for (int i = hq.size() - 1; i >= 0; i--) // put docs in array  
  25.         scoreDocs[i] = hq.pop();
然后在具体的线程类里的实现: 
  1. private static final class SearcherCallableNoSort implements Callable<TopDocs> {  
  2.   
  3.   private final Lock lock;  
  4.   private final IndexSearcher searcher;  
  5.   private final Weight weight;  
  6.   private final ScoreDoc after;  
  7.   private final int nDocs;  
  8.   private final HitQueue hq;  
  9.   private final LeafSlice slice;  
  10.   
  11.   public SearcherCallableNoSort(Lock lock, IndexSearcher searcher, LeafSlice slice,  Weight weight,  
  12.       ScoreDoc after, int nDocs, HitQueue hq) {  
  13.     this.lock = lock;  
  14.     this.searcher = searcher;  
  15.     this.weight = weight;  
  16.     this.after = after;  
  17.     this.nDocs = nDocs;  
  18.     this.hq = hq;  
  19.     this.slice = slice;  
  20.   }  
  21.   
  22.   @Override  
  23.   public TopDocs call() throws IOException {  
  24.     final TopDocs docs = searcher.search(Arrays.asList(slice.leaves), weight, after, nDocs);  
  25.     final ScoreDoc[] scoreDocs = docs.scoreDocs;  
  26.     //it would be so nice if we had a thread-safe insert   
  27.     lock.lock();  
  28.     try {  
  29.       for (int j = 0; j < scoreDocs.length; j++) { // merge scoreDocs into hq  
  30.         final ScoreDoc scoreDoc = scoreDocs[j];  
  31.         if (scoreDoc == hq.insertWithOverflow(scoreDoc)) {  
  32.           break;  
  33.         }  
  34.       }  
  35.     } finally {  
  36.       lock.unlock();  
  37.     }  
  38.     return docs;  
  39.   }  
通过源码,我们大概可以看出,这个提升,其实是利用了多线程的方式来完成的,通过实现Callable接口,以及重写其的call方法,最后通过公用的全局锁,来控制把检索到的结果集添加到公用的命中队列里,这样一来,一个检索,就被并行的分散到多个线程里,然后最后通过一个全局的容器,来获取所有线程检索的结果,由此以来,在某些场合就能大大提升检索性能。 
Please read full article from Lucene4.3进阶开发之柳暗花明( 六) - IndexSearcher

No comments:

Post a Comment

Labels

Algorithm (219) Lucene (130) LeetCode (97) Database (36) Data Structure (33) text mining (28) Solr (27) java (27) Mathematical Algorithm (26) Difficult Algorithm (25) Logic Thinking (23) Puzzles (23) Bit Algorithms (22) Math (21) List (20) Dynamic Programming (19) Linux (19) Tree (18) Machine Learning (15) EPI (11) Queue (11) Smart Algorithm (11) Operating System (9) Java Basic (8) Recursive Algorithm (8) Stack (8) Eclipse (7) Scala (7) Tika (7) J2EE (6) Monitoring (6) Trie (6) Concurrency (5) Geometry Algorithm (5) Greedy Algorithm (5) Mahout (5) MySQL (5) xpost (5) C (4) Interview (4) Vi (4) regular expression (4) to-do (4) C++ (3) Chrome (3) Divide and Conquer (3) Graph Algorithm (3) Permutation (3) Powershell (3) Random (3) Segment Tree (3) UIMA (3) Union-Find (3) Video (3) Virtualization (3) Windows (3) XML (3) Advanced Data Structure (2) Android (2) Bash (2) Classic Algorithm (2) Debugging (2) Design Pattern (2) Google (2) Hadoop (2) Java Collections (2) Markov Chains (2) Probabilities (2) Shell (2) Site (2) Web Development (2) Workplace (2) angularjs (2) .Net (1) Amazon Interview (1) Android Studio (1) Array (1) Boilerpipe (1) Book Notes (1) ChromeOS (1) Chromebook (1) Codility (1) Desgin (1) Design (1) Divide and Conqure (1) GAE (1) Google Interview (1) Great Stuff (1) Hash (1) High Tech Companies (1) Improving (1) LifeTips (1) Maven (1) Network (1) Performance (1) Programming (1) Resources (1) Sampling (1) Sed (1) Smart Thinking (1) Sort (1) Spark (1) Stanford NLP (1) System Design (1) Trove (1) VIP (1) tools (1)

Popular Posts