blob: 607641a8e15fcafbc373960c69e1f24acc2823ec [file] [log] [blame]
package org.apache.lucene.index;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Lucene" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Lucene", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.io.IOException;
import java.util.Hashtable;
import org.apache.lucene.store.Directory;
import org.apache.lucene.document.Document;
final class SegmentsReader extends IndexReader {
protected SegmentReader[] readers;
protected int[] starts; // 1st docno for each segment
private Hashtable normsCache = new Hashtable();
private int maxDoc = 0;
private int numDocs = -1;
SegmentsReader(SegmentReader[] r) throws IOException {
readers = r;
starts = new int[readers.length + 1]; // build starts array
for (int i = 0; i < readers.length; i++) {
starts[i] = maxDoc;
maxDoc += readers[i].maxDoc(); // compute maxDocs
}
starts[readers.length] = maxDoc;
}
public synchronized final int numDocs() {
if (numDocs == -1) { // check cache
int n = 0; // cache miss--recompute
for (int i = 0; i < readers.length; i++)
n += readers[i].numDocs(); // sum from readers
numDocs = n;
}
return numDocs;
}
public final int maxDoc() {
return maxDoc;
}
public final Document document(int n) throws IOException {
int i = readerIndex(n); // find segment num
return readers[i].document(n - starts[i]); // dispatch to segment reader
}
public final boolean isDeleted(int n) {
int i = readerIndex(n); // find segment num
return readers[i].isDeleted(n - starts[i]); // dispatch to segment reader
}
public synchronized final void delete(int n) throws IOException {
numDocs = -1; // invalidate cache
int i = readerIndex(n); // find segment num
readers[i].delete(n - starts[i]); // dispatch to segment reader
}
private final int readerIndex(int n) { // find reader for doc n:
int lo = 0; // search starts array
int hi = readers.length - 1; // for first element less
// than n, return its index
while (hi >= lo) {
int mid = (lo + hi) >> 1;
int midValue = starts[mid];
if (n < midValue)
hi = mid - 1;
else if (n > midValue)
lo = mid + 1;
else
return mid;
}
return hi;
}
public final synchronized byte[] norms(String field) throws IOException {
byte[] bytes = (byte[])normsCache.get(field);
if (bytes != null)
return bytes; // cache hit
bytes = new byte[maxDoc()];
for (int i = 0; i < readers.length; i++)
readers[i].norms(field, bytes, starts[i]);
normsCache.put(field, bytes); // update cache
return bytes;
}
public final TermEnum terms() throws IOException {
return new SegmentsTermEnum(readers, starts, null);
}
public final TermEnum terms(Term term) throws IOException {
return new SegmentsTermEnum(readers, starts, term);
}
public final int docFreq(Term t) throws IOException {
int total = 0; // sum freqs in segments
for (int i = 0; i < readers.length; i++)
total += readers[i].docFreq(t);
return total;
}
public final TermDocs termDocs(Term term) throws IOException {
return new SegmentsTermDocs(readers, starts, term);
}
public final TermPositions termPositions(Term term) throws IOException {
return new SegmentsTermPositions(readers, starts, term);
}
public final void close() throws IOException {
for (int i = 0; i < readers.length; i++)
readers[i].close();
}
}
class SegmentsTermEnum extends TermEnum {
private SegmentMergeQueue queue;
private Term term;
private int docFreq;
SegmentsTermEnum(SegmentReader[] readers, int[] starts, Term t)
throws IOException {
queue = new SegmentMergeQueue(readers.length);
for (int i = 0; i < readers.length; i++) {
SegmentReader reader = readers[i];
SegmentTermEnum termEnum;
if (t != null) {
termEnum = (SegmentTermEnum)reader.terms(t);
} else
termEnum = (SegmentTermEnum)reader.terms();
SegmentMergeInfo smi = new SegmentMergeInfo(starts[i], termEnum, reader);
if (t == null ? smi.next() : termEnum.term() != null)
queue.put(smi); // initialize queue
else
smi.close();
}
if (t != null && queue.size() > 0) {
SegmentMergeInfo top = (SegmentMergeInfo)queue.top();
term = top.termEnum.term();
docFreq = top.termEnum.docFreq();
}
}
public final boolean next() throws IOException {
SegmentMergeInfo top = (SegmentMergeInfo)queue.top();
if (top == null) {
term = null;
return false;
}
term = top.term;
docFreq = 0;
while (top != null && term.compareTo(top.term) == 0) {
queue.pop();
docFreq += top.termEnum.docFreq(); // increment freq
if (top.next())
queue.put(top); // restore queue
else
top.close(); // done with a segment
top = (SegmentMergeInfo)queue.top();
}
return true;
}
public final Term term() {
return term;
}
public final int docFreq() {
return docFreq;
}
public final void close() throws IOException {
queue.close();
}
}
class SegmentsTermDocs implements TermDocs {
protected SegmentReader[] readers;
protected int[] starts;
protected Term term;
protected int base = 0;
protected int pointer = 0;
SegmentsTermDocs(SegmentReader[] r, int[] s, Term t) {
readers = r;
starts = s;
term = t;
}
protected SegmentTermDocs current;
public final int doc() {
return base + current.doc;
}
public final int freq() {
return current.freq;
}
public final boolean next() throws IOException {
if (current != null && current.next()) {
return true;
} else if (pointer < readers.length) {
if (current != null)
current.close();
base = starts[pointer];
current = termDocs(readers[pointer++]);
return next();
} else
return false;
}
/** Optimized implementation. */
public final int read(final int[] docs, final int[] freqs)
throws IOException {
while (true) {
while (current == null) {
if (pointer < readers.length) { // try next segment
base = starts[pointer];
current = termDocs(readers[pointer++]);
} else {
return 0;
}
}
int end = current.read(docs, freqs);
if (end == 0) { // none left in segment
current.close();
current = null;
} else { // got some
final int b = base; // adjust doc numbers
for (int i = 0; i < end; i++)
docs[i] += b;
return end;
}
}
}
/** As yet unoptimized implementation. */
public boolean skipTo(int target) throws IOException {
do {
if (!next())
return false;
} while (target > doc());
return true;
}
protected SegmentTermDocs termDocs(SegmentReader reader)
throws IOException {
return (SegmentTermDocs)reader.termDocs(term);
}
public final void close() throws IOException {
if (current != null)
current.close();
}
}
class SegmentsTermPositions extends SegmentsTermDocs implements TermPositions {
SegmentsTermPositions(SegmentReader[] r, int[] s, Term t) {
super(r,s,t);
}
protected final SegmentTermDocs termDocs(SegmentReader reader)
throws IOException {
return (SegmentTermDocs)reader.termPositions(term);
}
public final int nextPosition() throws IOException {
return ((SegmentTermPositions)current).nextPosition();
}
}