/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Linq; using Lucene.Net.Support; using Document = Lucene.Net.Documents.Document; using FieldSelector = Lucene.Net.Documents.FieldSelector; using MultiTermDocs = Lucene.Net.Index.DirectoryReader.MultiTermDocs; using MultiTermEnum = Lucene.Net.Index.DirectoryReader.MultiTermEnum; using MultiTermPositions = Lucene.Net.Index.DirectoryReader.MultiTermPositions; using DefaultSimilarity = Lucene.Net.Search.DefaultSimilarity; namespace Lucene.Net.Index { /// An IndexReader which reads multiple indexes, appending /// their content. /// public class MultiReader:IndexReader, System.ICloneable { protected internal IndexReader[] subReaders; private int[] starts; // 1st docno for each segment private bool[] decrefOnClose; // remember which subreaders to decRef on close private System.Collections.Generic.IDictionary normsCache = new HashMap(); private int maxDoc = 0; private int numDocs = - 1; private bool hasDeletions = false; ///

Construct a MultiReader aggregating the named set of (sub)readers. /// Directory locking for delete, undeleteAll, and setNorm operations is /// left to the subreaders.

///

Note that all subreaders are closed if this Multireader is closed.

///

/// set of (sub)readers /// /// IOException public MultiReader(params IndexReader[] subReaders) { Initialize(subReaders, true); } ///

Construct a MultiReader aggregating the named set of (sub)readers. /// Directory locking for delete, undeleteAll, and setNorm operations is /// left to the subreaders.

///

/// indicates whether the subreaders should be closed /// when this MultiReader is closed /// /// set of (sub)readers /// /// IOException public MultiReader(IndexReader[] subReaders, bool closeSubReaders) { Initialize(subReaders, closeSubReaders); } private void Initialize(IndexReader[] subReaders, bool closeSubReaders) { // Deep copy this.subReaders = subReaders.ToArray(); starts = new int[subReaders.Length + 1]; // build starts array decrefOnClose = new bool[subReaders.Length]; for (int i = 0; i < subReaders.Length; i++) { starts[i] = maxDoc; maxDoc += subReaders[i].MaxDoc; // compute maxDocs if (!closeSubReaders) { subReaders[i].IncRef(); decrefOnClose[i] = true; } else { decrefOnClose[i] = false; } if (subReaders[i].HasDeletions) hasDeletions = true; } starts[subReaders.Length] = maxDoc; } /// Tries to reopen the subreaders. ///
/// If one or more subreaders could be re-opened (i. e. subReader.reopen() /// returned a new instance != subReader), then a new MultiReader instance /// is returned, otherwise this instance is returned. ///

/// A re-opened instance might share one or more subreaders with the old /// instance. Index modification operations result in undefined behavior /// when performed before the old instance is closed. /// (see ). ///

/// If subreaders are shared, then the reference count of those /// readers is increased to ensure that the subreaders remain open /// until the last referring reader is closed. /// ///

/// CorruptIndexException if the index is corrupt /// IOException if there is a low-level IO error public override IndexReader Reopen() { lock (this) { return DoReopen(false); } } /// Clones the subreaders. /// (see ). ///
///

/// If subreaders are shared, then the reference count of those /// readers is increased to ensure that the subreaders remain open /// until the last referring reader is closed. ///

public override System.Object Clone() { try { return DoReopen(true); } catch (System.Exception ex) { throw new System.SystemException(ex.Message, ex); } } /// If clone is true then we clone each of the subreaders /// /// /// New IndexReader, or same one (this) if /// reopen/clone is not necessary /// /// CorruptIndexException /// IOException protected internal virtual IndexReader DoReopen(bool doClone) { EnsureOpen(); bool reopened = false; IndexReader[] newSubReaders = new IndexReader[subReaders.Length]; bool success = false; try { for (int i = 0; i < subReaders.Length; i++) { if (doClone) newSubReaders[i] = (IndexReader) subReaders[i].Clone(); else newSubReaders[i] = subReaders[i].Reopen(); // if at least one of the subreaders was updated we remember that // and return a new MultiReader if (newSubReaders[i] != subReaders[i]) { reopened = true; } } success = true; } finally { if (!success && reopened) { for (int i = 0; i < newSubReaders.Length; i++) { if (newSubReaders[i] != subReaders[i]) { try { newSubReaders[i].Close(); } catch (System.IO.IOException) { // keep going - we want to clean up as much as possible } } } } } if (reopened) { bool[] newDecrefOnClose = new bool[subReaders.Length]; for (int i = 0; i < subReaders.Length; i++) { if (newSubReaders[i] == subReaders[i]) { newSubReaders[i].IncRef(); newDecrefOnClose[i] = true; } } MultiReader mr = new MultiReader(newSubReaders); mr.decrefOnClose = newDecrefOnClose; return mr; } else { return this; } } public override ITermFreqVector[] GetTermFreqVectors(int n) { EnsureOpen(); int i = ReaderIndex(n); // find segment num return subReaders[i].GetTermFreqVectors(n - starts[i]); // dispatch to segment } public override ITermFreqVector GetTermFreqVector(int n, System.String field) { EnsureOpen(); int i = ReaderIndex(n); // find segment num return subReaders[i].GetTermFreqVector(n - starts[i], field); } public override void GetTermFreqVector(int docNumber, System.String field, TermVectorMapper mapper) { EnsureOpen(); int i = ReaderIndex(docNumber); // find segment num subReaders[i].GetTermFreqVector(docNumber - starts[i], field, mapper); } public override void GetTermFreqVector(int docNumber, TermVectorMapper mapper) { EnsureOpen(); int i = ReaderIndex(docNumber); // find segment num subReaders[i].GetTermFreqVector(docNumber - starts[i], mapper); } public override bool IsOptimized() { return false; } public override int NumDocs() { // Don't call ensureOpen() here (it could affect performance) // NOTE: multiple threads may wind up init'ing // numDocs... but that's harmless if (numDocs == - 1) { // check cache int n = 0; // cache miss--recompute for (int i = 0; i < subReaders.Length; i++) n += subReaders[i].NumDocs(); // sum from readers numDocs = n; } return numDocs; } public override int MaxDoc { get { // Don't call ensureOpen() here (it could affect performance) return maxDoc; } } // inherit javadoc public override Document Document(int n, FieldSelector fieldSelector) { EnsureOpen(); int i = ReaderIndex(n); // find segment num return subReaders[i].Document(n - starts[i], fieldSelector); // dispatch to segment reader } public override bool IsDeleted(int n) { // Don't call ensureOpen() here (it could affect performance) int i = ReaderIndex(n); // find segment num return subReaders[i].IsDeleted(n - starts[i]); // dispatch to segment reader } public override bool HasDeletions { get { // Don't call ensureOpen() here (it could affect performance) return hasDeletions; } } protected internal override void DoDelete(int n) { numDocs = - 1; // invalidate cache int i = ReaderIndex(n); // find segment num subReaders[i].DeleteDocument(n - starts[i]); // dispatch to segment reader hasDeletions = true; } protected internal override void DoUndeleteAll() { for (int i = 0; i < subReaders.Length; i++) subReaders[i].UndeleteAll(); hasDeletions = false; numDocs = - 1; // invalidate cache } private int ReaderIndex(int n) { // find reader for doc n: return DirectoryReader.ReaderIndex(n, this.starts, this.subReaders.Length); } public override bool HasNorms(System.String field) { EnsureOpen(); for (int i = 0; i < subReaders.Length; i++) { if (subReaders[i].HasNorms(field)) return true; } return false; } public override byte[] Norms(System.String field) { lock (this) { EnsureOpen(); byte[] bytes = normsCache[field]; if (bytes != null) return bytes; // cache hit if (!HasNorms(field)) return null; bytes = new byte[MaxDoc]; for (int i = 0; i < subReaders.Length; i++) subReaders[i].Norms(field, bytes, starts[i]); normsCache[field] = bytes; // update cache return bytes; } } public override void Norms(System.String field, byte[] result, int offset) { lock (this) { EnsureOpen(); byte[] bytes = normsCache[field]; for (int i = 0; i < subReaders.Length; i++) // read from segments subReaders[i].Norms(field, result, offset + starts[i]); if (bytes == null && !HasNorms(field)) { for (int i = offset; i < result.Length; i++) { result[i] = (byte) DefaultSimilarity.EncodeNorm(1.0f); } } else if (bytes != null) { // cache hit Array.Copy(bytes, 0, result, offset, MaxDoc); } else { for (int i = 0; i < subReaders.Length; i++) { // read from segments subReaders[i].Norms(field, result, offset + starts[i]); } } } } protected internal override void DoSetNorm(int n, System.String field, byte value_Renamed) { lock (normsCache) { normsCache.Remove(field); // clear cache } int i = ReaderIndex(n); // find segment num subReaders[i].SetNorm(n - starts[i], field, value_Renamed); // dispatch } public override TermEnum Terms() { EnsureOpen(); return new MultiTermEnum(this, subReaders, starts, null); } public override TermEnum Terms(Term term) { EnsureOpen(); return new MultiTermEnum(this, subReaders, starts, term); } public override int DocFreq(Term t) { EnsureOpen(); int total = 0; // sum freqs in segments for (int i = 0; i < subReaders.Length; i++) total += subReaders[i].DocFreq(t); return total; } public override TermDocs TermDocs() { EnsureOpen(); return new MultiTermDocs(this, subReaders, starts); } public override TermPositions TermPositions() { EnsureOpen(); return new MultiTermPositions(this, subReaders, starts); } protected internal override void DoCommit(System.Collections.Generic.IDictionary commitUserData) { for (int i = 0; i < subReaders.Length; i++) subReaders[i].Commit(commitUserData); } protected internal override void DoClose() { lock (this) { for (int i = 0; i < subReaders.Length; i++) { if (decrefOnClose[i]) { subReaders[i].DecRef(); } else { subReaders[i].Close(); } } } // NOTE: only needed in case someone had asked for // FieldCache for top-level reader (which is generally // not a good idea): Lucene.Net.Search.FieldCache_Fields.DEFAULT.Purge(this); } public override System.Collections.Generic.ICollection GetFieldNames(IndexReader.FieldOption fieldNames) { EnsureOpen(); return DirectoryReader.GetFieldNames(fieldNames, this.subReaders); } /// Checks recursively if all subreaders are up to date. public override bool IsCurrent() { for (int i = 0; i < subReaders.Length; i++) { if (!subReaders[i].IsCurrent()) { return false; } } // all subreaders are up to date return true; } /// Not implemented. /// UnsupportedOperationException public override long Version { get { throw new System.NotSupportedException("MultiReader does not support this method."); } } public override IndexReader[] GetSequentialSubReaders() { return subReaders; } } }