2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 using Document = Mono.Lucene.Net.Documents.Document;
21 using FieldSelector = Mono.Lucene.Net.Documents.FieldSelector;
22 using MultiTermDocs = Mono.Lucene.Net.Index.DirectoryReader.MultiTermDocs;
23 using MultiTermEnum = Mono.Lucene.Net.Index.DirectoryReader.MultiTermEnum;
24 using MultiTermPositions = Mono.Lucene.Net.Index.DirectoryReader.MultiTermPositions;
25 using DefaultSimilarity = Mono.Lucene.Net.Search.DefaultSimilarity;
27 namespace Mono.Lucene.Net.Index
30 /// <summary>An IndexReader which reads multiple indexes, appending their content.
33 /// <version> $Id: MultiReader.java 782406 2009-06-07 16:31:18Z mikemccand $
35 public class MultiReader:IndexReader, System.ICloneable
37 protected internal IndexReader[] subReaders;
38 private int[] starts; // 1st docno for each segment
39 private bool[] decrefOnClose; // remember which subreaders to decRef on close
40 private System.Collections.IDictionary normsCache = new System.Collections.Hashtable();
41 private int maxDoc = 0;
42 private int numDocs = - 1;
43 private bool hasDeletions = false;
45 /// <summary> <p/>Construct a MultiReader aggregating the named set of (sub)readers.
46 /// Directory locking for delete, undeleteAll, and setNorm operations is
47 /// left to the subreaders. <p/>
48 /// <p/>Note that all subreaders are closed if this Multireader is closed.<p/>
50 /// <param name="subReaders">set of (sub)readers
52 /// <throws> IOException </throws>
53 public MultiReader(IndexReader[] subReaders)
55 Initialize(subReaders, true);
58 /// <summary> <p/>Construct a MultiReader aggregating the named set of (sub)readers.
59 /// Directory locking for delete, undeleteAll, and setNorm operations is
60 /// left to the subreaders. <p/>
62 /// <param name="closeSubReaders">indicates whether the subreaders should be closed
63 /// when this MultiReader is closed
65 /// <param name="subReaders">set of (sub)readers
67 /// <throws> IOException </throws>
68 public MultiReader(IndexReader[] subReaders, bool closeSubReaders)
70 Initialize(subReaders, closeSubReaders);
73 private void Initialize(IndexReader[] subReaders, bool closeSubReaders)
75 this.subReaders = new IndexReader[subReaders.Length];
76 subReaders.CopyTo(this.subReaders, 0);
77 starts = new int[subReaders.Length + 1]; // build starts array
78 decrefOnClose = new bool[subReaders.Length];
79 for (int i = 0; i < subReaders.Length; i++)
82 maxDoc += subReaders[i].MaxDoc(); // compute maxDocs
86 subReaders[i].IncRef();
87 decrefOnClose[i] = true;
91 decrefOnClose[i] = false;
94 if (subReaders[i].HasDeletions())
97 starts[subReaders.Length] = maxDoc;
100 /// <summary> Tries to reopen the subreaders.
102 /// If one or more subreaders could be re-opened (i. e. subReader.reopen()
103 /// returned a new instance != subReader), then a new MultiReader instance
104 /// is returned, otherwise this instance is returned.
106 /// A re-opened instance might share one or more subreaders with the old
107 /// instance. Index modification operations result in undefined behavior
108 /// when performed before the old instance is closed.
109 /// (see {@link IndexReader#Reopen()}).
111 /// If subreaders are shared, then the reference count of those
112 /// readers is increased to ensure that the subreaders remain open
113 /// until the last referring reader is closed.
116 /// <throws> CorruptIndexException if the index is corrupt </throws>
117 /// <throws> IOException if there is a low-level IO error </throws>
118 public override IndexReader Reopen()
122 return DoReopen(false);
126 /// <summary> Clones the subreaders.
127 /// (see {@link IndexReader#clone()}).
130 /// If subreaders are shared, then the reference count of those
131 /// readers is increased to ensure that the subreaders remain open
132 /// until the last referring reader is closed.
134 public override System.Object Clone()
138 return DoReopen(true);
140 catch (System.Exception ex)
142 throw new System.SystemException(ex.Message, ex);
146 /// <summary> If clone is true then we clone each of the subreaders</summary>
147 /// <param name="doClone">
149 /// <returns> New IndexReader, or same one (this) if
150 /// reopen/clone is not necessary
152 /// <throws> CorruptIndexException </throws>
153 /// <throws> IOException </throws>
154 protected internal virtual IndexReader DoReopen(bool doClone)
158 bool reopened = false;
159 IndexReader[] newSubReaders = new IndexReader[subReaders.Length];
161 bool success = false;
164 for (int i = 0; i < subReaders.Length; i++)
167 newSubReaders[i] = (IndexReader) subReaders[i].Clone();
169 newSubReaders[i] = subReaders[i].Reopen();
170 // if at least one of the subreaders was updated we remember that
171 // and return a new MultiReader
172 if (newSubReaders[i] != subReaders[i])
181 if (!success && reopened)
183 for (int i = 0; i < newSubReaders.Length; i++)
185 if (newSubReaders[i] != subReaders[i])
189 newSubReaders[i].Close();
191 catch (System.IO.IOException ignore)
193 // keep going - we want to clean up as much as possible
202 bool[] newDecrefOnClose = new bool[subReaders.Length];
203 for (int i = 0; i < subReaders.Length; i++)
205 if (newSubReaders[i] == subReaders[i])
207 newSubReaders[i].IncRef();
208 newDecrefOnClose[i] = true;
211 MultiReader mr = new MultiReader(newSubReaders);
212 mr.decrefOnClose = newDecrefOnClose;
213 mr.SetDisableFakeNorms(GetDisableFakeNorms());
222 public override TermFreqVector[] GetTermFreqVectors(int n)
225 int i = ReaderIndex(n); // find segment num
226 return subReaders[i].GetTermFreqVectors(n - starts[i]); // dispatch to segment
229 public override TermFreqVector GetTermFreqVector(int n, System.String field)
232 int i = ReaderIndex(n); // find segment num
233 return subReaders[i].GetTermFreqVector(n - starts[i], field);
237 public override void GetTermFreqVector(int docNumber, System.String field, TermVectorMapper mapper)
240 int i = ReaderIndex(docNumber); // find segment num
241 subReaders[i].GetTermFreqVector(docNumber - starts[i], field, mapper);
244 public override void GetTermFreqVector(int docNumber, TermVectorMapper mapper)
247 int i = ReaderIndex(docNumber); // find segment num
248 subReaders[i].GetTermFreqVector(docNumber - starts[i], mapper);
251 public override bool IsOptimized()
256 public override int NumDocs()
258 // Don't call ensureOpen() here (it could affect performance)
259 // NOTE: multiple threads may wind up init'ing
260 // numDocs... but that's harmless
264 int n = 0; // cache miss--recompute
265 for (int i = 0; i < subReaders.Length; i++)
266 n += subReaders[i].NumDocs(); // sum from readers
272 public override int MaxDoc()
274 // Don't call ensureOpen() here (it could affect performance)
279 public override Document Document(int n, FieldSelector fieldSelector)
282 int i = ReaderIndex(n); // find segment num
283 return subReaders[i].Document(n - starts[i], fieldSelector); // dispatch to segment reader
286 public override bool IsDeleted(int n)
288 // Don't call ensureOpen() here (it could affect performance)
289 int i = ReaderIndex(n); // find segment num
290 return subReaders[i].IsDeleted(n - starts[i]); // dispatch to segment reader
293 public override bool HasDeletions()
295 // Don't call ensureOpen() here (it could affect performance)
299 protected internal override void DoDelete(int n)
301 numDocs = - 1; // invalidate cache
302 int i = ReaderIndex(n); // find segment num
303 subReaders[i].DeleteDocument(n - starts[i]); // dispatch to segment reader
307 protected internal override void DoUndeleteAll()
309 for (int i = 0; i < subReaders.Length; i++)
310 subReaders[i].UndeleteAll();
312 hasDeletions = false;
313 numDocs = - 1; // invalidate cache
316 private int ReaderIndex(int n)
318 // find reader for doc n:
319 return DirectoryReader.ReaderIndex(n, this.starts, this.subReaders.Length);
322 public override bool HasNorms(System.String field)
325 for (int i = 0; i < subReaders.Length; i++)
327 if (subReaders[i].HasNorms(field))
334 private byte[] FakeNorms()
337 ones = SegmentReader.CreateFakeNorms(MaxDoc());
341 public override byte[] Norms(System.String field)
346 byte[] bytes = (byte[]) normsCache[field];
348 return bytes; // cache hit
349 if (!HasNorms(field))
350 return GetDisableFakeNorms()?null:FakeNorms();
352 bytes = new byte[MaxDoc()];
353 for (int i = 0; i < subReaders.Length; i++)
354 subReaders[i].Norms(field, bytes, starts[i]);
355 normsCache[field] = bytes; // update cache
360 public override void Norms(System.String field, byte[] result, int offset)
365 byte[] bytes = (byte[]) normsCache[field];
366 for (int i = 0; i < subReaders.Length; i++)
367 // read from segments
368 subReaders[i].Norms(field, result, offset + starts[i]);
370 if (bytes == null && !HasNorms(field))
372 for (int i = offset; i < result.Length; i++)
374 result[i] = (byte) DefaultSimilarity.EncodeNorm(1.0f);
377 else if (bytes != null)
380 Array.Copy(bytes, 0, result, offset, MaxDoc());
384 for (int i = 0; i < subReaders.Length; i++)
386 // read from segments
387 subReaders[i].Norms(field, result, offset + starts[i]);
393 protected internal override void DoSetNorm(int n, System.String field, byte value_Renamed)
395 lock (normsCache.SyncRoot)
397 normsCache.Remove(field); // clear cache
399 int i = ReaderIndex(n); // find segment num
400 subReaders[i].SetNorm(n - starts[i], field, value_Renamed); // dispatch
403 public override TermEnum Terms()
406 return new MultiTermEnum(this, subReaders, starts, null);
409 public override TermEnum Terms(Term term)
412 return new MultiTermEnum(this, subReaders, starts, term);
415 public override int DocFreq(Term t)
418 int total = 0; // sum freqs in segments
419 for (int i = 0; i < subReaders.Length; i++)
420 total += subReaders[i].DocFreq(t);
424 public override TermDocs TermDocs()
427 return new MultiTermDocs(this, subReaders, starts);
430 public override TermPositions TermPositions()
433 return new MultiTermPositions(this, subReaders, starts);
439 protected internal override void DoCommit()
444 protected internal override void DoCommit(System.Collections.Generic.IDictionary<string, string> commitUserData)
446 for (int i = 0; i < subReaders.Length; i++)
447 subReaders[i].Commit(commitUserData);
450 protected internal override void DoClose()
454 for (int i = 0; i < subReaders.Length; i++)
456 if (decrefOnClose[i])
458 subReaders[i].DecRef();
462 subReaders[i].Close();
467 // NOTE: only needed in case someone had asked for
468 // FieldCache for top-level reader (which is generally
470 Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.Purge(this);
473 public override System.Collections.Generic.ICollection<string> GetFieldNames(IndexReader.FieldOption fieldNames)
476 return DirectoryReader.GetFieldNames(fieldNames, this.subReaders);
479 /// <summary> Checks recursively if all subreaders are up to date. </summary>
480 public override bool IsCurrent()
482 for (int i = 0; i < subReaders.Length; i++)
484 if (!subReaders[i].IsCurrent())
490 // all subreaders are up to date
494 /// <summary>Not implemented.</summary>
495 /// <throws> UnsupportedOperationException </throws>
496 public override long GetVersion()
498 throw new System.NotSupportedException("MultiReader does not support this method.");
501 public override IndexReader[] GetSequentialSubReaders()