X-Git-Url: https://git.mdrn.pl/pylucene.git/blobdiff_plain/a2e61f0c04805cfcb8706176758d1283c7e3a55c..aaeed5504b982cf3545252ab528713250aa33eed:/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/index/MultiReader.java diff --git a/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/index/MultiReader.java b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/index/MultiReader.java new file mode 100644 index 0000000..8c6ea8f --- /dev/null +++ b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/index/MultiReader.java @@ -0,0 +1,477 @@ +package org.apache.lucene.index; + +/** + * 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. + */ + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.FieldSelector; +import org.apache.lucene.index.DirectoryReader.MultiTermDocs; +import org.apache.lucene.index.DirectoryReader.MultiTermEnum; +import org.apache.lucene.index.DirectoryReader.MultiTermPositions; +import org.apache.lucene.search.Similarity; +import org.apache.lucene.util.MapBackedSet; + +/** An IndexReader which reads multiple indexes, appending + * their content. */ +public class MultiReader extends IndexReader implements Cloneable { + protected IndexReader[] subReaders; + private int[] starts; // 1st docno for each segment + private boolean[] decrefOnClose; // remember which subreaders to decRef on close + private Map normsCache = new HashMap(); + private int maxDoc = 0; + private int numDocs = -1; + private boolean 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.

+ * @param subReaders set of (sub)readers + */ + public MultiReader(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.

+ * @param closeSubReaders indicates whether the subreaders should be closed + * when this MultiReader is closed + * @param subReaders set of (sub)readers + */ + public MultiReader(IndexReader[] subReaders, boolean closeSubReaders) { + initialize(subReaders, closeSubReaders); + } + + private void initialize(IndexReader[] subReaders, boolean closeSubReaders) { + this.subReaders = subReaders.clone(); + starts = new int[subReaders.length + 1]; // build starts array + decrefOnClose = new boolean[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; + readerFinishedListeners = new MapBackedSet(new ConcurrentHashMap()); + } + + /** + * Tries to reopen the subreaders. + *
+ * If one or more subreaders could be re-opened (i. e. IndexReader.openIfChanged(subReader) + * returned a new instance), 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 {@link IndexReader#openIfChanged}). + *

+ * 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. + * + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + */ + @Override + protected synchronized IndexReader doOpenIfChanged() throws CorruptIndexException, IOException { + return doOpenIfChanged(false); + } + + /** + * Clones the subreaders. + * (see {@link IndexReader#clone()}). + *
+ *

+ * 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. + */ + @Override + public synchronized Object clone() { + try { + return doOpenIfChanged(true); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + /** + * If clone is true then we clone each of the subreaders + * @param doClone + * @return New IndexReader, or null if open/clone is not necessary + * @throws CorruptIndexException + * @throws IOException + */ + protected IndexReader doOpenIfChanged(boolean doClone) throws CorruptIndexException, IOException { + ensureOpen(); + + boolean changed = false; + IndexReader[] newSubReaders = new IndexReader[subReaders.length]; + + boolean success = false; + try { + for (int i = 0; i < subReaders.length; i++) { + if (doClone) { + newSubReaders[i] = (IndexReader) subReaders[i].clone(); + changed = true; + } else { + final IndexReader newSubReader = IndexReader.openIfChanged(subReaders[i]); + if (newSubReader != null) { + newSubReaders[i] = newSubReader; + changed = true; + } else { + newSubReaders[i] = subReaders[i]; + } + } + } + success = true; + } finally { + if (!success && changed) { + for (int i = 0; i < newSubReaders.length; i++) { + if (newSubReaders[i] != subReaders[i]) { + try { + newSubReaders[i].close(); + } catch (IOException ignore) { + // keep going - we want to clean up as much as possible + } + } + } + } + } + + if (changed) { + boolean[] newDecrefOnClose = new boolean[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 null; + } + } + + @Override + public TermFreqVector[] getTermFreqVectors(int n) throws IOException { + ensureOpen(); + int i = readerIndex(n); // find segment num + return subReaders[i].getTermFreqVectors(n - starts[i]); // dispatch to segment + } + + @Override + public TermFreqVector getTermFreqVector(int n, String field) + throws IOException { + ensureOpen(); + int i = readerIndex(n); // find segment num + return subReaders[i].getTermFreqVector(n - starts[i], field); + } + + + @Override + public void getTermFreqVector(int docNumber, String field, TermVectorMapper mapper) throws IOException { + ensureOpen(); + int i = readerIndex(docNumber); // find segment num + subReaders[i].getTermFreqVector(docNumber - starts[i], field, mapper); + } + + @Override + public void getTermFreqVector(int docNumber, TermVectorMapper mapper) throws IOException { + ensureOpen(); + int i = readerIndex(docNumber); // find segment num + subReaders[i].getTermFreqVector(docNumber - starts[i], mapper); + } + + @Deprecated + @Override + public boolean isOptimized() { + ensureOpen(); + return false; + } + + @Override + public 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; + } + + @Override + public int maxDoc() { + // Don't call ensureOpen() here (it could affect performance) + return maxDoc; + } + + // inherit javadoc + @Override + public Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException { + ensureOpen(); + int i = readerIndex(n); // find segment num + return subReaders[i].document(n - starts[i], fieldSelector); // dispatch to segment reader + } + + @Override + public boolean 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 + } + + @Override + public boolean hasDeletions() { + ensureOpen(); + return hasDeletions; + } + + @Override + protected void doDelete(int n) throws CorruptIndexException, IOException { + numDocs = -1; // invalidate cache + int i = readerIndex(n); // find segment num + subReaders[i].deleteDocument(n - starts[i]); // dispatch to segment reader + hasDeletions = true; + } + + @Override + protected void doUndeleteAll() throws CorruptIndexException, IOException { + 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); + } + + @Override + public boolean hasNorms(String field) throws IOException { + ensureOpen(); + for (int i = 0; i < subReaders.length; i++) { + if (subReaders[i].hasNorms(field)) return true; + } + return false; + } + + @Override + public synchronized byte[] norms(String field) throws IOException { + ensureOpen(); + byte[] bytes = normsCache.get(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.put(field, bytes); // update cache + return bytes; + } + + @Override + public synchronized void norms(String field, byte[] result, int offset) + throws IOException { + ensureOpen(); + byte[] bytes = normsCache.get(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)) { + Arrays.fill(result, offset, result.length, Similarity.getDefault().encodeNormValue(1.0f)); + } else if (bytes != null) { // cache hit + System.arraycopy(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]); + } + } + } + + @Override + protected void doSetNorm(int n, String field, byte value) + throws CorruptIndexException, IOException { + synchronized (normsCache) { + normsCache.remove(field); // clear cache + } + int i = readerIndex(n); // find segment num + subReaders[i].setNorm(n-starts[i], field, value); // dispatch + } + + @Override + public TermEnum terms() throws IOException { + ensureOpen(); + if (subReaders.length == 1) { + // Optimize single segment case: + return subReaders[0].terms(); + } else { + return new MultiTermEnum(this, subReaders, starts, null); + } + } + + @Override + public TermEnum terms(Term term) throws IOException { + ensureOpen(); + if (subReaders.length == 1) { + // Optimize single segment case: + return subReaders[0].terms(term); + } else { + return new MultiTermEnum(this, subReaders, starts, term); + } + } + + @Override + public int docFreq(Term t) throws IOException { + ensureOpen(); + int total = 0; // sum freqs in segments + for (int i = 0; i < subReaders.length; i++) + total += subReaders[i].docFreq(t); + return total; + } + + @Override + public TermDocs termDocs() throws IOException { + ensureOpen(); + if (subReaders.length == 1) { + // Optimize single segment case: + return subReaders[0].termDocs(); + } else { + return new MultiTermDocs(this, subReaders, starts); + } + } + + @Override + public TermDocs termDocs(Term term) throws IOException { + ensureOpen(); + if (subReaders.length == 1) { + // Optimize single segment case: + return subReaders[0].termDocs(term); + } else { + return super.termDocs(term); + } + } + + @Override + public TermPositions termPositions() throws IOException { + ensureOpen(); + if (subReaders.length == 1) { + // Optimize single segment case: + return subReaders[0].termPositions(); + } else { + return new MultiTermPositions(this, subReaders, starts); + } + } + + @Override + protected void doCommit(Map commitUserData) throws IOException { + for (int i = 0; i < subReaders.length; i++) + subReaders[i].commit(commitUserData); + } + + @Override + protected synchronized void doClose() throws IOException { + for (int i = 0; i < subReaders.length; i++) { + if (decrefOnClose[i]) { + subReaders[i].decRef(); + } else { + subReaders[i].close(); + } + } + } + + @Override + public Collection getFieldNames (IndexReader.FieldOption fieldNames) { + ensureOpen(); + return DirectoryReader.getFieldNames(fieldNames, this.subReaders); + } + + /** + * Checks recursively if all subreaders are up to date. + */ + @Override + public boolean isCurrent() throws CorruptIndexException, IOException { + ensureOpen(); + for (int i = 0; i < subReaders.length; i++) { + if (!subReaders[i].isCurrent()) { + return false; + } + } + + // all subreaders are up to date + return true; + } + + /** Not implemented. + * @throws UnsupportedOperationException + */ + @Override + public long getVersion() { + throw new UnsupportedOperationException("MultiReader does not support this method."); + } + + @Override + public IndexReader[] getSequentialSubReaders() { + return subReaders; + } + + @Override + public void addReaderFinishedListener(ReaderFinishedListener listener) { + super.addReaderFinishedListener(listener); + for(IndexReader sub : subReaders) { + sub.addReaderFinishedListener(listener); + } + } + + @Override + public void removeReaderFinishedListener(ReaderFinishedListener listener) { + super.removeReaderFinishedListener(listener); + for(IndexReader sub : subReaders) { + sub.removeReaderFinishedListener(listener); + } + } +}