Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
MultiReader.cs
Go to the documentation of this file.
1 /*
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
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 
18 using System;
19 using System.Linq;
20 using Lucene.Net.Support;
21 using Document = Lucene.Net.Documents.Document;
22 using FieldSelector = Lucene.Net.Documents.FieldSelector;
23 using MultiTermDocs = Lucene.Net.Index.DirectoryReader.MultiTermDocs;
24 using MultiTermEnum = Lucene.Net.Index.DirectoryReader.MultiTermEnum;
25 using MultiTermPositions = Lucene.Net.Index.DirectoryReader.MultiTermPositions;
26 using DefaultSimilarity = Lucene.Net.Search.DefaultSimilarity;
27 
28 namespace Lucene.Net.Index
29 {
30 
31  /// <summary>An IndexReader which reads multiple indexes, appending
32  /// their content.
33  /// </summary>
34  public class MultiReader:IndexReader, System.ICloneable
35  {
36  protected internal IndexReader[] subReaders;
37  private int[] starts; // 1st docno for each segment
38  private bool[] decrefOnClose; // remember which subreaders to decRef on close
39  private System.Collections.Generic.IDictionary<string, byte[]> normsCache = new HashMap<string,byte[]>();
40  private int maxDoc = 0;
41  private int numDocs = - 1;
42  private bool hasDeletions = false;
43 
44  /// <summary> <p/>Construct a MultiReader aggregating the named set of (sub)readers.
45  /// Directory locking for delete, undeleteAll, and setNorm operations is
46  /// left to the subreaders. <p/>
47  /// <p/>Note that all subreaders are closed if this Multireader is closed.<p/>
48  /// </summary>
49  /// <param name="subReaders">set of (sub)readers
50  /// </param>
51  /// <throws> IOException </throws>
52  public MultiReader(params IndexReader[] subReaders)
53  {
54  Initialize(subReaders, true);
55  }
56 
57  /// <summary> <p/>Construct a MultiReader aggregating the named set of (sub)readers.
58  /// Directory locking for delete, undeleteAll, and setNorm operations is
59  /// left to the subreaders. <p/>
60  /// </summary>
61  /// <param name="closeSubReaders">indicates whether the subreaders should be closed
62  /// when this MultiReader is closed
63  /// </param>
64  /// <param name="subReaders">set of (sub)readers
65  /// </param>
66  /// <throws> IOException </throws>
67  public MultiReader(IndexReader[] subReaders, bool closeSubReaders)
68  {
69  Initialize(subReaders, closeSubReaders);
70  }
71 
72  private void Initialize(IndexReader[] subReaders, bool closeSubReaders)
73  {
74  // Deep copy
75  this.subReaders = subReaders.ToArray();
76  starts = new int[subReaders.Length + 1]; // build starts array
77  decrefOnClose = new bool[subReaders.Length];
78  for (int i = 0; i < subReaders.Length; i++)
79  {
80  starts[i] = maxDoc;
81  maxDoc += subReaders[i].MaxDoc; // compute maxDocs
82 
83  if (!closeSubReaders)
84  {
85  subReaders[i].IncRef();
86  decrefOnClose[i] = true;
87  }
88  else
89  {
90  decrefOnClose[i] = false;
91  }
92 
93  if (subReaders[i].HasDeletions)
94  hasDeletions = true;
95  }
96  starts[subReaders.Length] = maxDoc;
97  }
98 
99  /// <summary> Tries to reopen the subreaders.
100  /// <br/>
101  /// If one or more subreaders could be re-opened (i. e. subReader.reopen()
102  /// returned a new instance != subReader), then a new MultiReader instance
103  /// is returned, otherwise this instance is returned.
104  /// <p/>
105  /// A re-opened instance might share one or more subreaders with the old
106  /// instance. Index modification operations result in undefined behavior
107  /// when performed before the old instance is closed.
108  /// (see <see cref="IndexReader.Reopen()" />).
109  /// <p/>
110  /// If subreaders are shared, then the reference count of those
111  /// readers is increased to ensure that the subreaders remain open
112  /// until the last referring reader is closed.
113  ///
114  /// </summary>
115  /// <throws> CorruptIndexException if the index is corrupt </throws>
116  /// <throws> IOException if there is a low-level IO error </throws>
117  public override IndexReader Reopen()
118  {
119  lock (this)
120  {
121  return DoReopen(false);
122  }
123  }
124 
125  /// <summary> Clones the subreaders.
126  /// (see <see cref="IndexReader.Clone()" />).
127  /// <br/>
128  /// <p/>
129  /// If subreaders are shared, then the reference count of those
130  /// readers is increased to ensure that the subreaders remain open
131  /// until the last referring reader is closed.
132  /// </summary>
133  public override System.Object Clone()
134  {
135  try
136  {
137  return DoReopen(true);
138  }
139  catch (System.Exception ex)
140  {
141  throw new System.SystemException(ex.Message, ex);
142  }
143  }
144 
145  /// <summary> If clone is true then we clone each of the subreaders</summary>
146  /// <param name="doClone">
147  /// </param>
148  /// <returns> New IndexReader, or same one (this) if
149  /// reopen/clone is not necessary
150  /// </returns>
151  /// <throws> CorruptIndexException </throws>
152  /// <throws> IOException </throws>
153  protected internal virtual IndexReader DoReopen(bool doClone)
154  {
155  EnsureOpen();
156 
157  bool reopened = false;
158  IndexReader[] newSubReaders = new IndexReader[subReaders.Length];
159 
160  bool success = false;
161  try
162  {
163  for (int i = 0; i < subReaders.Length; i++)
164  {
165  if (doClone)
166  newSubReaders[i] = (IndexReader) subReaders[i].Clone();
167  else
168  newSubReaders[i] = subReaders[i].Reopen();
169  // if at least one of the subreaders was updated we remember that
170  // and return a new MultiReader
171  if (newSubReaders[i] != subReaders[i])
172  {
173  reopened = true;
174  }
175  }
176  success = true;
177  }
178  finally
179  {
180  if (!success && reopened)
181  {
182  for (int i = 0; i < newSubReaders.Length; i++)
183  {
184  if (newSubReaders[i] != subReaders[i])
185  {
186  try
187  {
188  newSubReaders[i].Close();
189  }
190  catch (System.IO.IOException)
191  {
192  // keep going - we want to clean up as much as possible
193  }
194  }
195  }
196  }
197  }
198 
199  if (reopened)
200  {
201  bool[] newDecrefOnClose = new bool[subReaders.Length];
202  for (int i = 0; i < subReaders.Length; i++)
203  {
204  if (newSubReaders[i] == subReaders[i])
205  {
206  newSubReaders[i].IncRef();
207  newDecrefOnClose[i] = true;
208  }
209  }
210  MultiReader mr = new MultiReader(newSubReaders);
211  mr.decrefOnClose = newDecrefOnClose;
212  return mr;
213  }
214  else
215  {
216  return this;
217  }
218  }
219 
220  public override ITermFreqVector[] GetTermFreqVectors(int n)
221  {
222  EnsureOpen();
223  int i = ReaderIndex(n); // find segment num
224  return subReaders[i].GetTermFreqVectors(n - starts[i]); // dispatch to segment
225  }
226 
227  public override ITermFreqVector GetTermFreqVector(int n, System.String field)
228  {
229  EnsureOpen();
230  int i = ReaderIndex(n); // find segment num
231  return subReaders[i].GetTermFreqVector(n - starts[i], field);
232  }
233 
234 
235  public override void GetTermFreqVector(int docNumber, System.String field, TermVectorMapper mapper)
236  {
237  EnsureOpen();
238  int i = ReaderIndex(docNumber); // find segment num
239  subReaders[i].GetTermFreqVector(docNumber - starts[i], field, mapper);
240  }
241 
242  public override void GetTermFreqVector(int docNumber, TermVectorMapper mapper)
243  {
244  EnsureOpen();
245  int i = ReaderIndex(docNumber); // find segment num
246  subReaders[i].GetTermFreqVector(docNumber - starts[i], mapper);
247  }
248 
249  public override bool IsOptimized()
250  {
251  return false;
252  }
253 
254  public override int NumDocs()
255  {
256  // Don't call ensureOpen() here (it could affect performance)
257  // NOTE: multiple threads may wind up init'ing
258  // numDocs... but that's harmless
259  if (numDocs == - 1)
260  {
261  // check cache
262  int n = 0; // cache miss--recompute
263  for (int i = 0; i < subReaders.Length; i++)
264  n += subReaders[i].NumDocs(); // sum from readers
265  numDocs = n;
266  }
267  return numDocs;
268  }
269 
270  public override int MaxDoc
271  {
272  get
273  {
274  // Don't call ensureOpen() here (it could affect performance)
275  return maxDoc;
276  }
277  }
278 
279  // inherit javadoc
280  public override Document Document(int n, FieldSelector fieldSelector)
281  {
282  EnsureOpen();
283  int i = ReaderIndex(n); // find segment num
284  return subReaders[i].Document(n - starts[i], fieldSelector); // dispatch to segment reader
285  }
286 
287  public override bool IsDeleted(int n)
288  {
289  // Don't call ensureOpen() here (it could affect performance)
290  int i = ReaderIndex(n); // find segment num
291  return subReaders[i].IsDeleted(n - starts[i]); // dispatch to segment reader
292  }
293 
294  public override bool HasDeletions
295  {
296  get
297  {
298  // Don't call ensureOpen() here (it could affect performance)
299  return hasDeletions;
300  }
301  }
302 
303  protected internal override void DoDelete(int n)
304  {
305  numDocs = - 1; // invalidate cache
306  int i = ReaderIndex(n); // find segment num
307  subReaders[i].DeleteDocument(n - starts[i]); // dispatch to segment reader
308  hasDeletions = true;
309  }
310 
311  protected internal override void DoUndeleteAll()
312  {
313  for (int i = 0; i < subReaders.Length; i++)
314  subReaders[i].UndeleteAll();
315 
316  hasDeletions = false;
317  numDocs = - 1; // invalidate cache
318  }
319 
320  private int ReaderIndex(int n)
321  {
322  // find reader for doc n:
323  return DirectoryReader.ReaderIndex(n, this.starts, this.subReaders.Length);
324  }
325 
326  public override bool HasNorms(System.String field)
327  {
328  EnsureOpen();
329  for (int i = 0; i < subReaders.Length; i++)
330  {
331  if (subReaders[i].HasNorms(field))
332  return true;
333  }
334  return false;
335  }
336 
337  public override byte[] Norms(System.String field)
338  {
339  lock (this)
340  {
341  EnsureOpen();
342  byte[] bytes = normsCache[field];
343  if (bytes != null)
344  return bytes; // cache hit
345  if (!HasNorms(field))
346  return null;
347 
348  bytes = new byte[MaxDoc];
349  for (int i = 0; i < subReaders.Length; i++)
350  subReaders[i].Norms(field, bytes, starts[i]);
351  normsCache[field] = bytes; // update cache
352  return bytes;
353  }
354  }
355 
356  public override void Norms(System.String field, byte[] result, int offset)
357  {
358  lock (this)
359  {
360  EnsureOpen();
361  byte[] bytes = normsCache[field];
362  for (int i = 0; i < subReaders.Length; i++)
363  // read from segments
364  subReaders[i].Norms(field, result, offset + starts[i]);
365 
366  if (bytes == null && !HasNorms(field))
367  {
368  for (int i = offset; i < result.Length; i++)
369  {
370  result[i] = (byte) DefaultSimilarity.EncodeNorm(1.0f);
371  }
372  }
373  else if (bytes != null)
374  {
375  // cache hit
376  Array.Copy(bytes, 0, result, offset, MaxDoc);
377  }
378  else
379  {
380  for (int i = 0; i < subReaders.Length; i++)
381  {
382  // read from segments
383  subReaders[i].Norms(field, result, offset + starts[i]);
384  }
385  }
386  }
387  }
388 
389  protected internal override void DoSetNorm(int n, System.String field, byte value_Renamed)
390  {
391  lock (normsCache)
392  {
393  normsCache.Remove(field); // clear cache
394  }
395  int i = ReaderIndex(n); // find segment num
396  subReaders[i].SetNorm(n - starts[i], field, value_Renamed); // dispatch
397  }
398 
399  public override TermEnum Terms()
400  {
401  EnsureOpen();
402  return new MultiTermEnum(this, subReaders, starts, null);
403  }
404 
405  public override TermEnum Terms(Term term)
406  {
407  EnsureOpen();
408  return new MultiTermEnum(this, subReaders, starts, term);
409  }
410 
411  public override int DocFreq(Term t)
412  {
413  EnsureOpen();
414  int total = 0; // sum freqs in segments
415  for (int i = 0; i < subReaders.Length; i++)
416  total += subReaders[i].DocFreq(t);
417  return total;
418  }
419 
420  public override TermDocs TermDocs()
421  {
422  EnsureOpen();
423  return new MultiTermDocs(this, subReaders, starts);
424  }
425 
426  public override TermPositions TermPositions()
427  {
428  EnsureOpen();
429  return new MultiTermPositions(this, subReaders, starts);
430  }
431 
432  protected internal override void DoCommit(System.Collections.Generic.IDictionary<string, string> commitUserData)
433  {
434  for (int i = 0; i < subReaders.Length; i++)
435  subReaders[i].Commit(commitUserData);
436  }
437 
438  protected internal override void DoClose()
439  {
440  lock (this)
441  {
442  for (int i = 0; i < subReaders.Length; i++)
443  {
444  if (decrefOnClose[i])
445  {
446  subReaders[i].DecRef();
447  }
448  else
449  {
450  subReaders[i].Close();
451  }
452  }
453  }
454 
455  // NOTE: only needed in case someone had asked for
456  // FieldCache for top-level reader (which is generally
457  // not a good idea):
458  Lucene.Net.Search.FieldCache_Fields.DEFAULT.Purge(this);
459  }
460 
461  public override System.Collections.Generic.ICollection<string> GetFieldNames(IndexReader.FieldOption fieldNames)
462  {
463  EnsureOpen();
464  return DirectoryReader.GetFieldNames(fieldNames, this.subReaders);
465  }
466 
467  /// <summary> Checks recursively if all subreaders are up to date. </summary>
468  public override bool IsCurrent()
469  {
470  for (int i = 0; i < subReaders.Length; i++)
471  {
472  if (!subReaders[i].IsCurrent())
473  {
474  return false;
475  }
476  }
477 
478  // all subreaders are up to date
479  return true;
480  }
481 
482  /// <summary>Not implemented.</summary>
483  /// <throws> UnsupportedOperationException </throws>
484  public override long Version
485  {
486  get { throw new System.NotSupportedException("MultiReader does not support this method."); }
487  }
488 
489  public override IndexReader[] GetSequentialSubReaders()
490  {
491  return subReaders;
492  }
493  }
494 }