MultiReader.java
001 package gate.creole.annic.apache.lucene.index;
002 
003 /**
004  * Copyright 2004 The Apache Software Foundation
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 
019 import java.io.IOException;
020 import java.util.Collection;
021 import java.util.HashSet;
022 import java.util.Hashtable;
023 import java.util.Iterator;
024 import java.util.Set;
025 
026 import gate.creole.annic.apache.lucene.document.Document;
027 import gate.creole.annic.apache.lucene.store.Directory;
028 
029 /** An IndexReader which reads multiple indexes, appending their content.
030  *
031  @version $Id: MultiReader.java 529 2004-10-05 11:55:26Z niraj $
032  */
033 @SuppressWarnings({"rawtypes","unchecked"})
034 public class MultiReader extends IndexReader {
035   private IndexReader[] subReaders;
036   private int[] starts;                           // 1st docno for each segment
037   private Hashtable normsCache = new Hashtable();
038   private int maxDoc = 0;
039   private int numDocs = -1;
040   private boolean hasDeletions = false;
041 
042  /**
043   <p>Construct a MultiReader aggregating the named set of (sub)readers.
044   * Directory locking for delete, undeleteAll, and setNorm operations is
045   * left to the subreaders. </p>
046   <p>Note that all subreaders are closed if this Multireader is closed.</p>
047   @param subReaders set of (sub)readers
048   @throws IOException
049   */
050   public MultiReader(IndexReader[] subReadersthrows IOException {
051     super(subReaders.length == null : subReaders[0].directory());
052     initialize(subReaders);
053   }
054 
055   /** Construct reading the named set of readers. */
056   MultiReader(Directory directory, SegmentInfos sis, boolean closeDirectory, IndexReader[] subReaders)
057     throws IOException {
058     super(directory, sis, closeDirectory);
059     initialize(subReaders);
060   }
061 
062   private void initialize(IndexReader[] subReadersthrows IOException{
063     this.subReaders = subReaders;
064     starts = new int[subReaders.length + 1];    // build starts array
065     for (int i = 0; i < subReaders.length; i++) {
066       starts[i= maxDoc;
067       maxDoc += subReaders[i].maxDoc();      // compute maxDocs
068 
069       if (subReaders[i].hasDeletions())
070         hasDeletions = true;
071     }
072     starts[subReaders.length= maxDoc;
073   }
074 
075 
076   /** Return an array of term frequency vectors for the specified document.
077    *  The array contains a vector for each vectorized field in the document.
078    *  Each vector vector contains term numbers and frequencies for all terms
079    *  in a given vectorized field.
080    *  If no such fields existed, the method returns null.
081    */
082   @Override
083   public TermFreqVector[] getTermFreqVectors(int nthrows IOException {
084     int i = readerIndex(n);        // find segment num
085     return subReaders[i].getTermFreqVectors(n - starts[i])// dispatch to segment
086   }
087 
088   @Override
089   public TermFreqVector getTermFreqVector(int n, String field)
090       throws IOException {
091     int i = readerIndex(n);        // find segment num
092     return subReaders[i].getTermFreqVector(n - starts[i], field);
093   }
094 
095   @Override
096   public synchronized int numDocs() {
097     if (numDocs == -1) {        // check cache
098       int n = 0;                // cache miss--recompute
099       for (int i = 0; i < subReaders.length; i++)
100         n += subReaders[i].numDocs();      // sum from readers
101       numDocs = n;
102     }
103     return numDocs;
104   }
105 
106   @Override
107   public int maxDoc() {
108     return maxDoc;
109   }
110 
111   @Override
112   public Document document(int nthrows IOException {
113     int i = readerIndex(n);                          // find segment num
114     return subReaders[i].document(n - starts[i]);    // dispatch to segment reader
115   }
116 
117   @Override
118   public boolean isDeleted(int n) {
119     int i = readerIndex(n);                           // find segment num
120     return subReaders[i].isDeleted(n - starts[i]);    // dispatch to segment reader
121   }
122 
123   @Override
124   public boolean hasDeletions() { return hasDeletions; }
125 
126   @Override
127   protected void doDelete(int nthrows IOException {
128     numDocs = -1;                             // invalidate cache
129     int i = readerIndex(n);                   // find segment num
130     subReaders[i].delete(n - starts[i]);      // dispatch to segment reader
131     hasDeletions = true;
132   }
133 
134   @Override
135   protected void doUndeleteAll() throws IOException {
136     for (int i = 0; i < subReaders.length; i++)
137       subReaders[i].undeleteAll();
138     hasDeletions = false;
139   }
140 
141   private int readerIndex(int n) {    // find reader for doc n:
142     int lo = 0;                                      // search starts array
143     int hi = subReaders.length - 1;                  // for first element less
144 
145     while (hi >= lo) {
146       int mid = (lo + hi>>> 1;
147       int midValue = starts[mid];
148       if (n < midValue)
149         hi = mid - 1;
150       else if (n > midValue)
151         lo = mid + 1;
152       else {                                      // found a match
153         while (mid+< subReaders.length && starts[mid+1== midValue) {
154           mid++;                                  // scan to last match
155         }
156         return mid;
157       }
158     }
159     return hi;
160   }
161 
162   @Override
163   public synchronized byte[] norms(String fieldthrows IOException {
164     byte[] bytes = (byte[])normsCache.get(field);
165     if (bytes != null)
166       return bytes;          // cache hit
167 
168     bytes = new byte[maxDoc()];
169     for (int i = 0; i < subReaders.length; i++)
170       subReaders[i].norms(field, bytes, starts[i]);
171     normsCache.put(field, bytes);      // update cache
172     return bytes;
173   }
174 
175   @Override
176   public synchronized void norms(String field, byte[] result, int offset)
177     throws IOException {
178     byte[] bytes = (byte[])normsCache.get(field);
179     if (bytes != null)                            // cache hit
180       System.arraycopy(bytes, 0, result, offset, maxDoc());
181 
182     for (int i = 0; i < subReaders.length; i++)      // read from segments
183       subReaders[i].norms(field, result, offset + starts[i]);
184   }
185 
186   @Override
187   protected void doSetNorm(int n, String field, byte value)
188     throws IOException {
189     normsCache.remove(field);                         // clear cache
190     int i = readerIndex(n);                           // find segment num
191     subReaders[i].setNorm(n-starts[i], field, value)// dispatch
192   }
193 
194   @Override
195   public TermEnum terms() throws IOException {
196     return new MultiTermEnum(subReaders, starts, null);
197   }
198 
199   @Override
200   public TermEnum terms(Term termthrows IOException {
201     return new MultiTermEnum(subReaders, starts, term);
202   }
203 
204   @Override
205   public int docFreq(Term tthrows IOException {
206     int total = 0;          // sum freqs in segments
207     for (int i = 0; i < subReaders.length; i++)
208       total += subReaders[i].docFreq(t);
209     return total;
210   }
211 
212   @Override
213   public TermDocs termDocs() throws IOException {
214     return new MultiTermDocs(subReaders, starts);
215   }
216 
217   @Override
218   public TermPositions termPositions() throws IOException {
219     return new MultiTermPositions(subReaders, starts);
220   }
221 
222   @Override
223   protected void doCommit() throws IOException {
224     for (int i = 0; i < subReaders.length; i++)
225       subReaders[i].commit();
226   }
227 
228   @Override
229   protected synchronized void doClose() throws IOException {
230     for (int i = 0; i < subReaders.length; i++)
231       subReaders[i].close();
232   }
233 
234   /**
235    @see IndexReader#getFieldNames()
236    */
237   @Override
238   public Collection getFieldNames() throws IOException {
239     // maintain a unique set of field names
240     Set fieldSet = new HashSet();
241     for (int i = 0; i < subReaders.length; i++) {
242       IndexReader reader = subReaders[i];
243       Collection names = reader.getFieldNames();
244       // iterate through the field names and add them to the set
245       for (Iterator iterator = names.iterator(); iterator.hasNext();) {
246         String s = (Stringiterator.next();
247         fieldSet.add(s);
248       }
249     }
250     return fieldSet;
251   }
252 
253   /**
254    @see IndexReader#getFieldNames(boolean)
255    */
256   @Override
257   public Collection getFieldNames(boolean indexedthrows IOException {
258     // maintain a unique set of field names
259     Set fieldSet = new HashSet();
260     for (int i = 0; i < subReaders.length; i++) {
261       IndexReader reader = subReaders[i];
262       Collection names = reader.getFieldNames(indexed);
263       fieldSet.addAll(names);
264     }
265     return fieldSet;
266   }
267 
268   @Override
269   public Collection getIndexedFieldNames(boolean storedTermVector) {
270     // maintain a unique set of field names
271     Set fieldSet = new HashSet();
272     for (int i = 0; i < subReaders.length; i++) {
273       IndexReader reader = subReaders[i];
274       Collection names = reader.getIndexedFieldNames(storedTermVector);
275       fieldSet.addAll(names);
276     }
277     return fieldSet;
278   }
279 
280 }
281 
282 class MultiTermEnum extends TermEnum {
283   private SegmentMergeQueue queue;
284 
285   private Term term;
286   private int docFreq;
287 
288   public MultiTermEnum(IndexReader[] readers, int[] starts, Term t)
289     throws IOException {
290     queue = new SegmentMergeQueue(readers.length);
291     for (int i = 0; i < readers.length; i++) {
292       IndexReader reader = readers[i];
293       TermEnum termEnum;
294 
295       if (t != null) {
296         termEnum = reader.terms(t);
297       else
298         termEnum = reader.terms();
299 
300       SegmentMergeInfo smi = new SegmentMergeInfo(starts[i], termEnum, reader);
301       if (t == null ? smi.next() : termEnum.term() != null)
302         queue.put(smi);          // initialize queue
303       else
304         smi.close();
305     }
306 
307     if (t != null && queue.size() 0) {
308       next();
309     }
310   }
311 
312   @Override
313   public boolean next() throws IOException {
314     SegmentMergeInfo top = (SegmentMergeInfo)queue.top();
315     if (top == null) {
316       term = null;
317       return false;
318     }
319 
320     term = top.term;
321     docFreq = 0;
322     while (top != null && term.indexCompareTo(top.term== 0) {
323       queue.pop();
324       docFreq += top.termEnum.docFreq();    // increment freq
325       if (top.next())
326         queue.put(top);          // restore queue
327       else
328         top.close();          // done with a segment
329       top = (SegmentMergeInfo)queue.top();
330     }
331     return true;
332   }
333 
334   @Override
335   public Term term() {
336     return term;
337   }
338 
339   @Override
340   public int docFreq() {
341     return docFreq;
342   }
343 
344   @Override
345   public void close() throws IOException {
346     queue.close();
347   }
348 }
349 
350 class MultiTermDocs implements TermDocs {
351   protected IndexReader[] readers;
352   protected int[] starts;
353   protected Term term;
354 
355   protected int base = 0;
356   protected int pointer = 0;
357 
358   private TermDocs[] readerTermDocs;
359   protected TermDocs current;              // == readerTermDocs[pointer]
360 
361   public MultiTermDocs(IndexReader[] r, int[] s) {
362     readers = r;
363     starts = s;
364 
365     readerTermDocs = new TermDocs[r.length];
366   }
367 
368   @Override
369   public int doc() {
370     return base + current.doc();
371   }
372   @Override
373   public int freq() {
374     return current.freq();
375   }
376 
377   @Override
378   public void seek(Term term) {
379     this.term = term;
380     this.base = 0;
381     this.pointer = 0;
382     this.current = null;
383   }
384 
385   @Override
386   public void seek(TermEnum termEnumthrows IOException {
387     seek(termEnum.term());
388   }
389 
390   @Override
391   public boolean next() throws IOException {
392     if (current != null && current.next()) {
393       return true;
394     else if (pointer < readers.length) {
395       base = starts[pointer];
396       current = termDocs(pointer++);
397       return next();
398     else
399       return false;
400   }
401 
402   /** Optimized implementation. */
403   @Override
404   public int read(final int[] docs, final int[] freqsthrows IOException {
405     while (true) {
406       while (current == null) {
407         if (pointer < readers.length) {      // try next segment
408           base = starts[pointer];
409           current = termDocs(pointer++);
410         else {
411           return 0;
412         }
413       }
414       int end = current.read(docs, freqs);
415       if (end == 0) {          // none left in segment
416         current = null;
417       else {            // got some
418         final int b = base;        // adjust doc numbers
419         for (int i = 0; i < end; i++)
420          docs[i+= b;
421         return end;
422       }
423     }
424   }
425 
426   /** As yet unoptimized implementation. */
427   @Override
428   public boolean skipTo(int targetthrows IOException {
429     do {
430       if (!next())
431         return false;
432     while (target > doc());
433       return true;
434   }
435 
436   private TermDocs termDocs(int ithrows IOException {
437     if (term == null)
438       return null;
439     TermDocs result = readerTermDocs[i];
440     if (result == null)
441       result = readerTermDocs[i= termDocs(readers[i]);
442     result.seek(term);
443     return result;
444   }
445 
446   protected TermDocs termDocs(IndexReader reader)
447     throws IOException {
448     return reader.termDocs();
449   }
450 
451   @Override
452   public void close() throws IOException {
453     for (int i = 0; i < readerTermDocs.length; i++) {
454       if (readerTermDocs[i!= null)
455         readerTermDocs[i].close();
456     }
457   }
458 }
459 
460 class MultiTermPositions extends MultiTermDocs implements TermPositions {
461   public MultiTermPositions(IndexReader[] r, int[] s) {
462     super(r,s);
463   }
464 
465   @Override
466   protected TermDocs termDocs(IndexReader readerthrows IOException {
467     return reader.termPositions();
468   }
469 
470   @Override
471   public int nextPosition() throws IOException {
472     return ((TermPositions)current).nextPosition();
473   }
474 
475 }