| 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(); |
| } |
| } |