Index: lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java --- lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java Sat Jul 09 10:52:11 2011 -0400 @@ -249,10 +249,11 @@ AtomicReaderContext context = getLeafContextForField(field); final Spans spans; + Weight.ScorerContext scorerContext = Weight.ScorerContext.def().acceptOnlyDocs(context.reader.getLiveDocs()); if (mustRewriteQuery) { - spans = queries.get(field).getSpans(context); + spans = queries.get(field).getSpans(context, scorerContext); } else { - spans = spanQuery.getSpans(context); + spans = spanQuery.getSpans(context, scorerContext); } Index: lucene/src/java/org/apache/lucene/search/BooleanQuery.java --- lucene/src/java/org/apache/lucene/search/BooleanQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/BooleanQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -294,9 +294,10 @@ List prohibited = new ArrayList(); List optional = new ArrayList(); Iterator cIter = clauses.iterator(); + final ScorerContext subScorerContext = ScorerContext.def().acceptOnlyDocs(scorerContext.acceptOnlyDocs); for (Weight w : weights) { BooleanClause c = cIter.next(); - Scorer subScorer = w.scorer(context, ScorerContext.def()); + Scorer subScorer = w.scorer(context, subScorerContext); if (subScorer == null) { if (c.isRequired()) { return null; Index: lucene/src/java/org/apache/lucene/search/CachingSpanFilter.java --- lucene/src/java/org/apache/lucene/search/CachingSpanFilter.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/CachingSpanFilter.java Sat Jul 09 10:52:11 2011 -0400 @@ -16,11 +16,12 @@ */ +import java.io.IOException; + +import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.util.Bits; - -import java.io.IOException; +import org.apache.lucene.util.OpenBitSet; /** * Wraps another SpanFilter's result and caches it. The purpose is to allow @@ -83,6 +84,10 @@ missCount++; result = filter.bitSpans(context); + // nocommit hackish: + if (result.getDocIdSet() instanceof OpenBitSet) { + ((OpenBitSet) result.getDocIdSet()).setIncludesDeletedDocs(cache.deletesMode == CachingWrapperFilter.DeletesMode.RECACHE); + } cache.put(coreKey, delCoreKey, result); return result; Index: lucene/src/java/org/apache/lucene/search/CachingWrapperFilter.java --- lucene/src/java/org/apache/lucene/search/CachingWrapperFilter.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/CachingWrapperFilter.java Sat Jul 09 10:52:11 2011 -0400 @@ -23,6 +23,7 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.util.OpenBitSet; import org.apache.lucene.util.OpenBitSetDISI; import org.apache.lucene.util.Bits; @@ -76,7 +77,7 @@ // after de-serialize transient Map cache; - private final DeletesMode deletesMode; + final DeletesMode deletesMode; public FilterCache(DeletesMode deletesMode) { this.deletesMode = deletesMode; @@ -210,6 +211,20 @@ // cache miss docIdSet = docIdSetToCache(filter.getDocIdSet(context), reader); + if (docIdSet instanceof OpenBitSet) { + final OpenBitSet obs = (OpenBitSet) docIdSet; + final int setCount = (int) obs.cardinality(); + + // nocommit how/where to make this hardwired 1% + // settable...? + + if (setCount > 0 && (reader.maxDoc()/setCount) < 100) { + obs.setUseRandomAccess(true); + obs.setIncludesDeletedDocs(cache.deletesMode != DeletesMode.IGNORE); + System.out.println("DORAND " + (cache.deletesMode != DeletesMode.IGNORE)); + } + } + if (docIdSet != null) { cache.put(coreKey, delCoreKey, docIdSet); } Index: lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java --- lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -129,6 +129,9 @@ public Scorer scorer(AtomicReaderContext context, ScorerContext scorerContext) throws IOException { final DocIdSetIterator disi; if (filter != null) { + // nocommit we lose scorerContext.acceptOnlyDocs!! + //if (scorerContext.acceptOnlyDocs != context.reader.getLiveDocs()) { + //} assert query == null; final DocIdSet dis = filter.getDocIdSet(context); if (dis == null) { Index: lucene/src/java/org/apache/lucene/search/DocIdSet.java --- lucene/src/java/org/apache/lucene/search/DocIdSet.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/DocIdSet.java Sat Jul 09 10:52:11 2011 -0400 @@ -19,6 +19,8 @@ import java.io.IOException; +import org.apache.lucene.util.Bits; + /** * A DocIdSet contains a set of doc ids. Implementing classes must * only implement {@link #iterator} to provide access to the set. @@ -46,6 +48,11 @@ public boolean isCacheable() { return true; } + + @Override + public Bits getRandomAccessBits() { + return null; + } }; /** Provides a {@link DocIdSetIterator} to access the set. @@ -64,4 +71,23 @@ public boolean isCacheable() { return false; } + + /** Only used if {@link #getRandomAccessBits} returns non-null result; + * return true if the Bits instance has already factored + * in deletions. */ + // nocommit better name...? somewhere else...? move to + // subclass...? + public boolean bitsIncludesDeletedDocs() { + return false; + } + + /** Return a Bits impl if this DocIdSet should be applied + * via random-access (because the underlying bit set + * implementation supports random access, and the filter + * is dense enough), instead of {@link DocIdSetIterator}. */ + // nocommit better name...? somewhere else...? move to + // subclass...? + public Bits getRandomAccessBits() { + return null; + } } Index: lucene/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java --- lucene/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java Sat Jul 09 10:52:11 2011 -0400 @@ -505,7 +505,7 @@ /** Returns the current numeric parser ({@code null} for {@code T} is {@code String}} */ public FieldCache.Parser getParser() { return parser; } - static abstract class FieldCacheDocIdSet extends DocIdSet { + static abstract class FieldCacheDocIdSet extends DocIdSet implements Bits { private final IndexReader reader; private final boolean canIgnoreDeletedDocs; @@ -530,6 +530,26 @@ } @Override + public Bits getRandomAccessBits() { + return this; + } + + @Override + public boolean bitsIncludesDeletedDocs() { + return canIgnoreDeletedDocs; + } + + @Override + public boolean get(int docID) { + return matchDoc(docID); + } + + @Override + public int length() { + return reader.maxDoc(); + } + + @Override public DocIdSetIterator iterator() throws IOException { final Bits liveDocs = canIgnoreDeletedDocs ? null : reader.getLiveDocs(); Index: lucene/src/java/org/apache/lucene/search/IndexSearcher.java --- lucene/src/java/org/apache/lucene/search/IndexSearcher.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/IndexSearcher.java Sat Jul 09 10:52:11 2011 -0400 @@ -32,13 +32,15 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.FieldSelector; import org.apache.lucene.index.CorruptIndexException; -import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.IndexReader.ReaderContext; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.search.Weight.ScorerContext; import org.apache.lucene.store.Directory; import org.apache.lucene.store.NIOFSDirectory; // javadoc +import org.apache.lucene.util.AndBits; +import org.apache.lucene.util.Bits; import org.apache.lucene.util.ReaderUtil; import org.apache.lucene.util.ThreadInterruptedException; @@ -537,13 +539,14 @@ // TODO: should we make this // threaded...? the Collector could be sync'd? - ScorerContext scorerContext = ScorerContext.def().scoreDocsInOrder(true).topScorer(true); // always use single thread: if (filter == null) { + ScorerContext scorerContext = ScorerContext.def().scoreDocsInOrder(true).topScorer(true); for (int i = 0; i < leaves.length; i++) { // search each subreader collector.setNextReader(leaves[i]); - scorerContext = scorerContext.scoreDocsInOrder(!collector.acceptsDocsOutOfOrder()); - Scorer scorer = weight.scorer(leaves[i], scorerContext); + Scorer scorer = weight.scorer(leaves[i], + scorerContext.scoreDocsInOrder(!collector.acceptsDocsOutOfOrder()) + .acceptOnlyDocs(leaves[i].reader.getLiveDocs())); if (scorer != null) { scorer.score(collector); } @@ -560,14 +563,7 @@ final Filter filter, final Collector collector) throws IOException { assert filter != null; - - Scorer scorer = weight.scorer(context, ScorerContext.def()); - if (scorer == null) { - return; - } - - int docID = scorer.docID(); - assert docID == -1 || docID == DocIdSetIterator.NO_MORE_DOCS; + //System.out.println("IS.searchWithFilter r=" + context.reader); // CHECKME: use ConjunctionScorer here? DocIdSet filterDocIdSet = filter.getDocIdSet(context); @@ -575,29 +571,74 @@ // this means the filter does not accept any documents. return; } + + ScorerContext scorerContext = ScorerContext.def(); - DocIdSetIterator filterIter = filterDocIdSet.iterator(); - if (filterIter == null) { - // this means the filter does not accept any documents. + Bits liveDocs = context.reader.getLiveDocs(); + //System.out.println(" liveDocs=" + liveDocs); + + Bits acceptOnlyDocs = filterDocIdSet.getRandomAccessBits(); + //System.out.println(" accOnlyDocs=" + acceptOnlyDocs); + + if (acceptOnlyDocs != null) { + + // Filter by random-access: we push the filter all the + // way down to the atomic scorers, so the bits are + // applied just like deleted docs: + if (liveDocs != null && !filterDocIdSet.bitsIncludesDeletedDocs()) { + // TODO: we could swap order of these two if we knew + // which is more restrictive?: + acceptOnlyDocs = new AndBits(acceptOnlyDocs, liveDocs); + //System.out.println(" and w/ liveDocs"); + } else { + //System.out.println(" use accOnlyDocs"); + } + + scorerContext = scorerContext.scoreDocsInOrder(!collector.acceptsDocsOutOfOrder()).topScorer(true).acceptOnlyDocs(acceptOnlyDocs); + } else if (liveDocs != null) { + scorerContext = scorerContext.acceptOnlyDocs(liveDocs); + } + + Scorer scorer = weight.scorer(context, scorerContext); + if (scorer == null) { return; } - int filterDoc = filterIter.nextDoc(); - int scorerDoc = scorer.advance(filterDoc); - - collector.setScorer(scorer); - while (true) { - if (scorerDoc == filterDoc) { - // Check if scorer has exhausted, only before collecting. - if (scorerDoc == DocIdSetIterator.NO_MORE_DOCS) { - break; + + if (acceptOnlyDocs != null) { + //System.out.println("IS: filter down low f=" + scorerContext.acceptOnlyDocs); + // Filter "down low": + collector.setNextReader(context); + scorer.score(collector); + } else { + //System.out.println("IS: filter up high"); + // Filter "up high": + int docID = scorer.docID(); + assert docID == -1 || docID == DocIdSetIterator.NO_MORE_DOCS; + DocIdSetIterator filterIter = filterDocIdSet.iterator(); + if (filterIter == null) { + // this means the filter does not accept any documents. + return; + } + int filterDoc = filterIter.nextDoc(); + int scorerDoc = scorer.advance(filterDoc); + + // Filter by iteration: + collector.setScorer(scorer); + while (true) { + if (scorerDoc == filterDoc) { + // Check if scorer has exhausted, only before collecting. + if (scorerDoc == DocIdSetIterator.NO_MORE_DOCS) { + break; + } + //System.out.println(" c=" + scorerDoc); + collector.collect(scorerDoc); + filterDoc = filterIter.nextDoc(); + scorerDoc = scorer.advance(filterDoc); + } else if (scorerDoc > filterDoc) { + filterDoc = filterIter.advance(scorerDoc); + } else { + scorerDoc = scorer.advance(filterDoc); } - collector.collect(scorerDoc); - filterDoc = filterIter.nextDoc(); - scorerDoc = scorer.advance(filterDoc); - } else if (scorerDoc > filterDoc) { - filterDoc = filterIter.advance(scorerDoc); - } else { - scorerDoc = scorer.advance(filterDoc); } } } Index: lucene/src/java/org/apache/lucene/search/MatchAllDocsQuery.java --- lucene/src/java/org/apache/lucene/search/MatchAllDocsQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/MatchAllDocsQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -38,9 +38,9 @@ private final int maxDoc; private final Bits liveDocs; - MatchAllScorer(IndexReader reader, Weight w, float score) throws IOException { + MatchAllScorer(IndexReader reader, Bits acceptOnlyDocs, Weight w, float score) throws IOException { super(w); - liveDocs = reader.getLiveDocs(); + liveDocs = acceptOnlyDocs; this.score = score; maxDoc = reader.maxDoc(); } @@ -105,7 +105,7 @@ @Override public Scorer scorer(AtomicReaderContext context, ScorerContext scorerContext) throws IOException { - return new MatchAllScorer(context.reader, this, queryWeight); + return new MatchAllScorer(context.reader, scorerContext.acceptOnlyDocs, this, queryWeight); } @Override Index: lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java --- lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -167,7 +167,8 @@ if (termArrays.size() == 0) // optimize zero-term case return null; final IndexReader reader = context.reader; - final Bits liveDocs = reader.getLiveDocs(); + + final Bits liveDocs = scorerContext.acceptOnlyDocs; PhraseQuery.PostingsAndFreq[] postingsFreqs = new PhraseQuery.PostingsAndFreq[termArrays.size()]; @@ -178,7 +179,7 @@ int docFreq; if (terms.length > 1) { - postingsEnum = new UnionDocsAndPositionsEnum(reader, terms); + postingsEnum = new UnionDocsAndPositionsEnum(scorerContext.acceptOnlyDocs, reader, terms); // coarse -- this overcounts since a given doc can // have more than one terms: @@ -188,7 +189,7 @@ } } else { final Term term = terms[0]; - postingsEnum = reader.termPositionsEnum(liveDocs, + postingsEnum = reader.termPositionsEnum(scorerContext.acceptOnlyDocs, term.field(), term.bytes()); @@ -431,17 +432,17 @@ private DocsQueue _queue; private IntQueue _posList; - public UnionDocsAndPositionsEnum(IndexReader indexReader, Term[] terms) throws IOException { + public UnionDocsAndPositionsEnum(Bits acceptOnlyDocs, IndexReader indexReader, Term[] terms) throws IOException { List docsEnums = new LinkedList(); - final Bits liveDocs = indexReader.getLiveDocs(); + for (int i = 0; i < terms.length; i++) { - DocsAndPositionsEnum postings = indexReader.termPositionsEnum(liveDocs, + DocsAndPositionsEnum postings = indexReader.termPositionsEnum(acceptOnlyDocs, terms[i].field(), terms[i].bytes()); if (postings != null) { docsEnums.add(postings); } else { - if (indexReader.termDocsEnum(liveDocs, terms[i].field(), terms[i].bytes()) != null) { + if (indexReader.termDocsEnum(acceptOnlyDocs, terms[i].field(), terms[i].bytes()) != null) { // term does exist, but has no positions throw new IllegalStateException("field \"" + terms[i].field() + "\" was indexed with Field.omitTermFreqAndPositions=true; cannot run PhraseQuery (term=" + terms[i].text() + ")"); } Index: lucene/src/java/org/apache/lucene/search/MultiTermQueryWrapperFilter.java --- lucene/src/java/org/apache/lucene/search/MultiTermQueryWrapperFilter.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/MultiTermQueryWrapperFilter.java Sat Jul 09 10:52:11 2011 -0400 @@ -147,6 +147,7 @@ } while (termsEnum.next() != null); // System.out.println(" done termCount=" + termCount); + // nocommit set random access here? query.incTotalNumberOfTerms(termCount); return bitSet; } else { Index: lucene/src/java/org/apache/lucene/search/PhraseQuery.java --- lucene/src/java/org/apache/lucene/search/PhraseQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/PhraseQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -211,7 +211,6 @@ if (terms.size() == 0) // optimize zero-term case return null; final IndexReader reader = context.reader; - final Bits liveDocs = reader.getLiveDocs(); PostingsAndFreq[] postingsFreqs = new PostingsAndFreq[terms.size()]; for (int i = 0; i < terms.size(); i++) { final Term t = terms.get(i); @@ -220,14 +219,14 @@ assert termNotInReader(reader, field, t.bytes()) : "no termstate found but term exists in reader"; return null; } - DocsAndPositionsEnum postingsEnum = reader.termPositionsEnum(liveDocs, + DocsAndPositionsEnum postingsEnum = reader.termPositionsEnum(scorerContext.acceptOnlyDocs, t.field(), t.bytes(), state); // PhraseQuery on a field that did not index // positions. if (postingsEnum == null) { - assert (reader.termDocsEnum(liveDocs, t.field(), t.bytes(), state) != null) : "termstate found but no term exists in reader"; + assert (reader.termDocsEnum(scorerContext.acceptOnlyDocs, t.field(), t.bytes(), state) != null) : "termstate found but no term exists in reader"; // term does exist, but has no positions throw new IllegalStateException("field \"" + t.field() + "\" was indexed with Field.omitTermFreqAndPositions=true; cannot run PhraseQuery (term=" + t.text() + ")"); } Index: lucene/src/java/org/apache/lucene/search/QueryWrapperFilter.java --- lucene/src/java/org/apache/lucene/search/QueryWrapperFilter.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/QueryWrapperFilter.java Sat Jul 09 10:52:11 2011 -0400 @@ -56,7 +56,7 @@ return new DocIdSet() { @Override public DocIdSetIterator iterator() throws IOException { - return weight.scorer(privateContext, ScorerContext.def()); + return weight.scorer(privateContext, ScorerContext.def().acceptOnlyDocs(context.reader.getLiveDocs())); } @Override public boolean isCacheable() { return false; } Index: lucene/src/java/org/apache/lucene/search/SpanFilter.java --- lucene/src/java/org/apache/lucene/search/SpanFilter.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/SpanFilter.java Sat Jul 09 10:52:11 2011 -0400 @@ -31,6 +31,7 @@ /** Returns a SpanFilterResult with true for documents which should be permitted in search results, and false for those that should not and Spans for where the true docs match. * @param context The {@link AtomicReaderContext} to load position and DocIdSet information from + * @param context The {@link Weight#ScorerContext} to load position and DocIdSet information from * @return A {@link SpanFilterResult} * @throws java.io.IOException if there was an issue accessing the necessary information * */ Index: lucene/src/java/org/apache/lucene/search/SpanQueryFilter.java --- lucene/src/java/org/apache/lucene/search/SpanQueryFilter.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/SpanQueryFilter.java Sat Jul 09 10:52:11 2011 -0400 @@ -61,7 +61,7 @@ public SpanFilterResult bitSpans(AtomicReaderContext context) throws IOException { final OpenBitSet bits = new OpenBitSet(context.reader.maxDoc()); - Spans spans = query.getSpans(context); + Spans spans = query.getSpans(context, Weight.ScorerContext.def().acceptOnlyDocs(context.reader.getLiveDocs())); List tmp = new ArrayList(20); int currentDoc = -1; SpanFilterResult.PositionInfo currentInfo = null; Index: lucene/src/java/org/apache/lucene/search/TermQuery.java --- lucene/src/java/org/apache/lucene/search/TermQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/TermQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -80,7 +80,7 @@ assert termNotInReader(reader, field, term.bytes()) : "no termstate found but term exists in reader"; return null; } - final DocsEnum docs = reader.termDocsEnum(reader.getLiveDocs(), field, term.bytes(), state); + final DocsEnum docs = reader.termDocsEnum(scorerContext.acceptOnlyDocs, field, term.bytes(), state); assert docs != null; return new TermScorer(this, docs, similarity.exactDocScorer(stats, field, context)); } Index: lucene/src/java/org/apache/lucene/search/Weight.java --- lucene/src/java/org/apache/lucene/search/Weight.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/Weight.java Sat Jul 09 10:52:11 2011 -0400 @@ -19,9 +19,10 @@ import java.io.IOException; -import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.IndexReader.ReaderContext; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.util.Bits; /** * Expert: Calculate query weights and build query scorers. @@ -138,8 +139,11 @@ */ public final boolean topScorer; - - private static final ScorerContext DEFAULT_CONTEXT = new ScorerContext(true, false); + /** If non-null, the returned scorer should filter + * according to this {@link Bits} instance. */ + public final Bits acceptOnlyDocs; + + private static final ScorerContext DEFAULT_CONTEXT = new ScorerContext(true, false, null); /** * Returns a default {@link ScorerContext} template initialized with: @@ -152,9 +156,10 @@ return DEFAULT_CONTEXT; } - private ScorerContext(boolean scoreDocsInOrder, boolean topScorer) { + private ScorerContext(boolean scoreDocsInOrder, boolean topScorer, Bits acceptOnlyDocs) { this.scoreDocsInOrder = scoreDocsInOrder; this.topScorer = topScorer; + this.acceptOnlyDocs = acceptOnlyDocs; } /** @@ -168,7 +173,7 @@ if (this.scoreDocsInOrder == scoreDocsInOrder) { return this; } - return new ScorerContext(scoreDocsInOrder, topScorer); + return new ScorerContext(scoreDocsInOrder, topScorer, acceptOnlyDocs); } /** @@ -182,7 +187,21 @@ if (this.topScorer == topScorer) { return this; } - return new ScorerContext(scoreDocsInOrder, topScorer); + return new ScorerContext(scoreDocsInOrder, topScorer, acceptOnlyDocs); + } + + /** + * Creates and returns a copy of this context with the given value for + * {@link #topScorer} and returns a new instance of + * {@link ScorerContext} iff the given value differs from the + * {@link #topScorer}. Otherwise, this method has no effect and + * returns this instance. + */ + public ScorerContext acceptOnlyDocs(Bits acceptOnlyDocs) { + if (this.acceptOnlyDocs == acceptOnlyDocs) { + return this; + } + return new ScorerContext(scoreDocsInOrder, topScorer, acceptOnlyDocs); } } } Index: lucene/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java --- lucene/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -146,7 +146,7 @@ @Override public Scorer scorer(AtomicReaderContext context, ScorerContext scorerContext) throws IOException { - return new PayloadNearSpanScorer(query.getSpans(context), this, + return new PayloadNearSpanScorer(query.getSpans(context, scorerContext), this, similarity, similarity.sloppyDocScorer(stats, query.getField(), context)); } Index: lucene/src/java/org/apache/lucene/search/payloads/PayloadSpanUtil.java --- lucene/src/java/org/apache/lucene/search/payloads/PayloadSpanUtil.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/payloads/PayloadSpanUtil.java Sat Jul 09 10:52:11 2011 -0400 @@ -23,9 +23,9 @@ import java.util.Iterator; import java.util.List; -import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.IndexReader.ReaderContext; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; @@ -35,6 +35,7 @@ import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; import org.apache.lucene.search.spans.SpanNearQuery; import org.apache.lucene.search.spans.SpanOrQuery; import org.apache.lucene.search.spans.SpanQuery; @@ -176,7 +177,7 @@ throws IOException { final AtomicReaderContext[] leaves = ReaderUtil.leaves(context); for (AtomicReaderContext atomicReaderContext : leaves) { - final Spans spans = query.getSpans(atomicReaderContext); + final Spans spans = query.getSpans(atomicReaderContext, Weight.ScorerContext.def().acceptOnlyDocs(atomicReaderContext.reader.getLiveDocs())); while (spans.next() == true) { if (spans.isPayloadAvailable()) { Collection payload = spans.getPayload(); Index: lucene/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java --- lucene/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -78,7 +78,7 @@ @Override public Scorer scorer(AtomicReaderContext context, ScorerContext scorerContext) throws IOException { - return new PayloadTermSpanScorer((TermSpans) query.getSpans(context), + return new PayloadTermSpanScorer((TermSpans) query.getSpans(context, scorerContext), this, similarity, similarity.sloppyDocScorer(stats, query.getField(), context)); } Index: lucene/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java --- lucene/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -92,8 +92,8 @@ // ...this is done to be more consistent with things like SpanFirstQuery @Override - public Spans getSpans(AtomicReaderContext context) throws IOException { - return maskedQuery.getSpans(context); + public Spans getSpans(AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException { + return maskedQuery.getSpans(context, scorerContext); } @Override Index: lucene/src/java/org/apache/lucene/search/spans/NearSpansOrdered.java --- lucene/src/java/org/apache/lucene/search/spans/NearSpansOrdered.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/NearSpansOrdered.java Sat Jul 09 10:52:11 2011 -0400 @@ -17,18 +17,19 @@ * limitations under the License. */ -import org.apache.lucene.index.IndexReader.AtomicReaderContext; -import org.apache.lucene.util.ArrayUtil; - import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Collection; import java.util.Set; +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.ArrayUtil; + /** A Spans that is formed from the ordered subspans of a SpanNearQuery * where the subspans do not overlap and have a maximum slop between them. *

@@ -77,11 +78,11 @@ private SpanNearQuery query; private boolean collectPayloads = true; - public NearSpansOrdered(SpanNearQuery spanNearQuery, AtomicReaderContext context) throws IOException { - this(spanNearQuery, context, true); + public NearSpansOrdered(SpanNearQuery spanNearQuery, AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException { + this(spanNearQuery, context, scorerContext, true); } - public NearSpansOrdered(SpanNearQuery spanNearQuery, AtomicReaderContext context, boolean collectPayloads) + public NearSpansOrdered(SpanNearQuery spanNearQuery, AtomicReaderContext context, Weight.ScorerContext scorerContext, boolean collectPayloads) throws IOException { if (spanNearQuery.getClauses().length < 2) { throw new IllegalArgumentException("Less than 2 clauses: " @@ -94,7 +95,7 @@ matchPayload = new LinkedList(); subSpansByDoc = new Spans[clauses.length]; for (int i = 0; i < clauses.length; i++) { - subSpans[i] = clauses[i].getSpans(context); + subSpans[i] = clauses[i].getSpans(context, scorerContext); subSpansByDoc[i] = subSpans[i]; // used in toSameDoc() } query = spanNearQuery; // kept for toString() only. Index: lucene/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java --- lucene/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java Sat Jul 09 10:52:11 2011 -0400 @@ -17,15 +17,16 @@ * limitations under the License. */ -import org.apache.lucene.index.IndexReader.AtomicReaderContext; -import org.apache.lucene.util.PriorityQueue; - import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.HashSet; + +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.PriorityQueue; /** * Similar to {@link NearSpansOrdered}, but for the unordered case. @@ -131,7 +132,7 @@ } - public NearSpansUnordered(SpanNearQuery query, AtomicReaderContext context) + public NearSpansUnordered(SpanNearQuery query, AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException { this.query = query; this.slop = query.getSlop(); @@ -141,7 +142,7 @@ subSpans = new Spans[clauses.length]; for (int i = 0; i < clauses.length; i++) { SpansCell cell = - new SpansCell(clauses[i].getSpans(context), i); + new SpansCell(clauses[i].getSpans(context, scorerContext), i); ordered.add(cell); subSpans[i] = cell.spans; } Index: lucene/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java --- lucene/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java Sat Jul 09 10:52:11 2011 -0400 @@ -19,14 +19,15 @@ import java.io.IOException; +import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; // javadocs only import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.ScoringRewrite; import org.apache.lucene.search.TopTermsRewrite; -import org.apache.lucene.search.ScoringRewrite; -import org.apache.lucene.search.BooleanClause.Occur; // javadocs only +import org.apache.lucene.search.Weight; import org.apache.lucene.util.TermContext; /** @@ -89,7 +90,7 @@ } @Override - public Spans getSpans(AtomicReaderContext context) throws IOException { + public Spans getSpans(AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException { throw new UnsupportedOperationException("Query should have been rewritten"); } Index: lucene/src/java/org/apache/lucene/search/spans/SpanNearQuery.java --- lucene/src/java/org/apache/lucene/search/spans/SpanNearQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/SpanNearQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -18,18 +18,16 @@ */ import java.io.IOException; - - -import java.util.List; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Set; - +import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; import org.apache.lucene.util.ToStringUtils; /** Matches spans which are near one another. One can specify slop, the @@ -117,16 +115,16 @@ } @Override - public Spans getSpans(final AtomicReaderContext context) throws IOException { + public Spans getSpans(final AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException { if (clauses.size() == 0) // optimize 0-clause case - return new SpanOrQuery(getClauses()).getSpans(context); + return new SpanOrQuery(getClauses()).getSpans(context, scorerContext); if (clauses.size() == 1) // optimize 1-clause case - return clauses.get(0).getSpans(context); + return clauses.get(0).getSpans(context, scorerContext); return inOrder - ? (Spans) new NearSpansOrdered(this, context, collectPayloads) - : (Spans) new NearSpansUnordered(this, context); + ? (Spans) new NearSpansOrdered(this, context, scorerContext, collectPayloads) + : (Spans) new NearSpansUnordered(this, context, scorerContext); } @Override Index: lucene/src/java/org/apache/lucene/search/spans/SpanNotQuery.java --- lucene/src/java/org/apache/lucene/search/spans/SpanNotQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/SpanNotQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -17,17 +17,18 @@ * limitations under the License. */ -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexReader.AtomicReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; -import org.apache.lucene.util.ToStringUtils; - import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Set; +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.ToStringUtils; + /** Removes matches which overlap with another SpanQuery. */ public class SpanNotQuery extends SpanQuery implements Cloneable { private SpanQuery include; @@ -71,76 +72,76 @@ public Object clone() { SpanNotQuery spanNotQuery = new SpanNotQuery((SpanQuery)include.clone(),(SpanQuery) exclude.clone()); spanNotQuery.setBoost(getBoost()); - return spanNotQuery; + return spanNotQuery; } @Override - public Spans getSpans(final AtomicReaderContext context) throws IOException { + public Spans getSpans(final AtomicReaderContext context, final Weight.ScorerContext scorerContext) throws IOException { return new Spans() { - private Spans includeSpans = include.getSpans(context); - private boolean moreInclude = true; + private Spans includeSpans = include.getSpans(context, scorerContext); + private boolean moreInclude = true; - private Spans excludeSpans = exclude.getSpans(context); - private boolean moreExclude = excludeSpans.next(); + private Spans excludeSpans = exclude.getSpans(context, scorerContext); + private boolean moreExclude = excludeSpans.next(); - @Override - public boolean next() throws IOException { - if (moreInclude) // move to next include - moreInclude = includeSpans.next(); + @Override + public boolean next() throws IOException { + if (moreInclude) // move to next include + moreInclude = includeSpans.next(); - while (moreInclude && moreExclude) { + while (moreInclude && moreExclude) { - if (includeSpans.doc() > excludeSpans.doc()) // skip exclude - moreExclude = excludeSpans.skipTo(includeSpans.doc()); - - while (moreExclude // while exclude is before - && includeSpans.doc() == excludeSpans.doc() - && excludeSpans.end() <= includeSpans.start()) { - moreExclude = excludeSpans.next(); // increment exclude - } - - if (!moreExclude // if no intersection - || includeSpans.doc() != excludeSpans.doc() - || includeSpans.end() <= excludeSpans.start()) - break; // we found a match - - moreInclude = includeSpans.next(); // intersected: keep scanning - } - return moreInclude; - } - - @Override - public boolean skipTo(int target) throws IOException { - if (moreInclude) // skip include - moreInclude = includeSpans.skipTo(target); - - if (!moreInclude) - return false; - - if (moreExclude // skip exclude - && includeSpans.doc() > excludeSpans.doc()) + if (includeSpans.doc() > excludeSpans.doc()) // skip exclude moreExclude = excludeSpans.skipTo(includeSpans.doc()); - while (moreExclude // while exclude is before + while (moreExclude // while exclude is before && includeSpans.doc() == excludeSpans.doc() && excludeSpans.end() <= includeSpans.start()) { - moreExclude = excludeSpans.next(); // increment exclude + moreExclude = excludeSpans.next(); // increment exclude } if (!moreExclude // if no intersection - || includeSpans.doc() != excludeSpans.doc() - || includeSpans.end() <= excludeSpans.start()) - return true; // we found a match + || includeSpans.doc() != excludeSpans.doc() + || includeSpans.end() <= excludeSpans.start()) + break; // we found a match - return next(); // scan to next match + moreInclude = includeSpans.next(); // intersected: keep scanning + } + return moreInclude; + } + + @Override + public boolean skipTo(int target) throws IOException { + if (moreInclude) // skip include + moreInclude = includeSpans.skipTo(target); + + if (!moreInclude) + return false; + + if (moreExclude // skip exclude + && includeSpans.doc() > excludeSpans.doc()) + moreExclude = excludeSpans.skipTo(includeSpans.doc()); + + while (moreExclude // while exclude is before + && includeSpans.doc() == excludeSpans.doc() + && excludeSpans.end() <= includeSpans.start()) { + moreExclude = excludeSpans.next(); // increment exclude } - @Override - public int doc() { return includeSpans.doc(); } - @Override - public int start() { return includeSpans.start(); } - @Override - public int end() { return includeSpans.end(); } + if (!moreExclude // if no intersection + || includeSpans.doc() != excludeSpans.doc() + || includeSpans.end() <= excludeSpans.start()) + return true; // we found a match + + return next(); // scan to next match + } + + @Override + public int doc() { return includeSpans.doc(); } + @Override + public int start() { return includeSpans.start(); } + @Override + public int end() { return includeSpans.end(); } // TODO: Remove warning after API has been finalized @Override Index: lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java --- lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -18,19 +18,19 @@ */ import java.io.IOException; - +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import java.util.List; -import java.util.Collection; -import java.util.ArrayList; -import java.util.Iterator; import java.util.Set; +import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.Term; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; import org.apache.lucene.util.PriorityQueue; import org.apache.lucene.util.ToStringUtils; -import org.apache.lucene.search.Query; /** Matches the union of its clauses.*/ public class SpanOrQuery extends SpanQuery implements Cloneable { @@ -163,9 +163,9 @@ } @Override - public Spans getSpans(final AtomicReaderContext context) throws IOException { + public Spans getSpans(final AtomicReaderContext context, final Weight.ScorerContext scorerContext) throws IOException { if (clauses.size() == 1) // optimize 1-clause case - return (clauses.get(0)).getSpans(context); + return (clauses.get(0)).getSpans(context, scorerContext); return new Spans() { private SpanQueue queue = null; @@ -174,8 +174,8 @@ queue = new SpanQueue(clauses.size()); Iterator i = clauses.iterator(); while (i.hasNext()) { - Spans spans = i.next().getSpans(context); - if ( ((target == -1) && spans.next()) + Spans spans = i.next().getSpans(context, scorerContext); + if (((target == -1) && spans.next()) || ((target != -1) && spans.skipTo(target))) { queue.add(spans); } Index: lucene/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java --- lucene/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -17,16 +17,17 @@ */ -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexReader.AtomicReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; - import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Set; +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; + /** * @@ -81,8 +82,8 @@ protected abstract AcceptStatus acceptPosition(Spans spans) throws IOException; @Override - public Spans getSpans(final AtomicReaderContext context) throws IOException { - return new PositionCheckSpan(context); + public Spans getSpans(final AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException { + return new PositionCheckSpan(context, scorerContext); } @@ -106,8 +107,8 @@ protected class PositionCheckSpan extends Spans { private Spans spans; - public PositionCheckSpan(AtomicReaderContext context) throws IOException { - spans = match.getSpans(context); + public PositionCheckSpan(AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException { + spans = match.getSpans(context, scorerContext); } @Override Index: lucene/src/java/org/apache/lucene/search/spans/SpanQuery.java --- lucene/src/java/org/apache/lucene/search/spans/SpanQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/SpanQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -28,7 +28,7 @@ public abstract class SpanQuery extends Query { /** Expert: Returns the matches for this query in an index. Used internally * to search for spans. */ - public abstract Spans getSpans(AtomicReaderContext context) throws IOException; + public abstract Spans getSpans(AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException; /** Returns the name of the field matched by this query.*/ public abstract String getField(); Index: lucene/src/java/org/apache/lucene/search/spans/SpanTermQuery.java --- lucene/src/java/org/apache/lucene/search/spans/SpanTermQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/SpanTermQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -21,6 +21,7 @@ import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.index.DocsAndPositionsEnum; +import org.apache.lucene.search.Weight; import org.apache.lucene.util.ToStringUtils; import java.io.IOException; @@ -81,9 +82,9 @@ } @Override - public Spans getSpans(final AtomicReaderContext context) throws IOException { + public Spans getSpans(final AtomicReaderContext context, final Weight.ScorerContext scorerContext) throws IOException { final IndexReader reader = context.reader; - final DocsAndPositionsEnum postings = reader.termPositionsEnum(reader.getLiveDocs(), + final DocsAndPositionsEnum postings = reader.termPositionsEnum(scorerContext.acceptOnlyDocs, term.field(), term.bytes()); Index: lucene/src/java/org/apache/lucene/search/spans/SpanWeight.java --- lucene/src/java/org/apache/lucene/search/spans/SpanWeight.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/spans/SpanWeight.java Sat Jul 09 10:52:11 2011 -0400 @@ -67,7 +67,7 @@ @Override public Scorer scorer(AtomicReaderContext context, ScorerContext scorerContext) throws IOException { - return new SpanScorer(query.getSpans(context), this, similarity, similarity.sloppyDocScorer(stats, query.getField(), context)); + return new SpanScorer(query.getSpans(context, scorerContext), this, similarity, similarity.sloppyDocScorer(stats, query.getField(), context)); } @Override Index: lucene/src/java/org/apache/lucene/util/AndBits.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ lucene/src/java/org/apache/lucene/util/AndBits.java Sat Jul 09 10:52:11 2011 -0400 @@ -0,0 +1,45 @@ +package org.apache.lucene.util; + +/** + * 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. + */ + +/** + * @lucene.experimental + */ + +public final class AndBits implements Bits { + private final Bits bits1; + private final Bits bits2; + + public AndBits(Bits bits1, Bits bits2) { + this.bits1 = bits1; + this.bits2 = bits2; + // TODO: unfortunately OpenBitSet lies about its length + // (rounds up to nearest N = 0 (mod 64)): + //if (bits1.length() != bits2.length()) { + //throw new IllegalArgumentException("bit lengths differ: " + bits1.length() + " vs " + bits2.length()); + //} + } + + public boolean get(int index) { + return bits1.get(index) && bits2.get(index); + } + + public int length() { + return Math.min(bits1.length(), bits2.length()); + } +} Index: lucene/src/java/org/apache/lucene/util/OpenBitSet.java --- lucene/src/java/org/apache/lucene/util/OpenBitSet.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/java/org/apache/lucene/util/OpenBitSet.java Sat Jul 09 10:52:11 2011 -0400 @@ -126,7 +126,12 @@ } /** Returns the current capacity in bits (1 greater than the index of the last bit) */ - public long capacity() { return bits.length << 6; } + public long capacity() { + return bits.length << 6; + // nocommit -- cannot do this in general: numBits isn't + // always maintained when asserts are off + // return (int) numBits; + } /** * Returns the current capacity of this set. Included for @@ -139,6 +144,9 @@ @Override public int length() { return bits.length << 6; + // nocommit -- cannot do this in general: numBits isn't + // always maintained when asserts are off + // return (int) numBits; } /** Returns true if there are no set bits */ @@ -902,6 +910,35 @@ // empty sets from returning 0, which is too common. return (int)((h>>32) ^ h) + 0x98761234; } + + // nocommit -- default to false? + private boolean includesDeletions = true; + + /** @lucene.experimental */ + public void setIncludesDeletedDocs(boolean v) { + includesDeletions = v; + } + + /** @lucene.experimental */ + @Override + public boolean bitsIncludesDeletedDocs() { + return includesDeletions; + } + + // nocommit -- default to false? + // nocommit -- would be nice if during testing we could + // default this randomly: + private boolean useRandomAccess = true; + + /** @lucene.experimental */ + public void setUseRandomAccess(boolean v) { + useRandomAccess = v; + } + + @Override + public Bits getRandomAccessBits() { + return useRandomAccess ? this : null; + } } Index: lucene/src/test-framework/org/apache/lucene/search/CheckHits.java --- lucene/src/test-framework/org/apache/lucene/search/CheckHits.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/test-framework/org/apache/lucene/search/CheckHits.java Sat Jul 09 10:52:11 2011 -0400 @@ -100,10 +100,9 @@ for (int i = -1; i < 2; i++) { actual.clear(); - IndexSearcher s = QueryUtils.wrapUnderlyingReader - (random, searcher, i); + IndexSearcher s = QueryUtils.wrapUnderlyingReader(random, searcher, i); s.search(query, c); - Assert.assertEquals("Wrap Reader " + i + ": " + + Assert.assertEquals("Wrap Reader " + i + ": s=" + s + " query=" + query.toString(defaultFieldName), correct, actual); FieldCache.DEFAULT.purge(s.getIndexReader()); // our wrapping can create insanity otherwise Index: lucene/src/test-framework/org/apache/lucene/search/QueryUtils.java --- lucene/src/test-framework/org/apache/lucene/search/QueryUtils.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/test-framework/org/apache/lucene/search/QueryUtils.java Sat Jul 09 10:52:11 2011 -0400 @@ -256,7 +256,7 @@ try { if (scorer == null) { Weight w = s.createNormalizedWeight(q); - scorer = w.scorer(readerContextArray[leafPtr], ScorerContext.def()); + scorer = w.scorer(readerContextArray[leafPtr], ScorerContext.def().acceptOnlyDocs(readerContextArray[leafPtr].reader.getLiveDocs())); } int op = order[(opidx[0]++) % order.length]; @@ -301,7 +301,7 @@ final IndexReader previousReader = lastReader[0]; IndexSearcher indexSearcher = LuceneTestCase.newSearcher(previousReader); Weight w = indexSearcher.createNormalizedWeight(q); - Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), ScorerContext.def()); + Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), ScorerContext.def().acceptOnlyDocs(previousReader.getLiveDocs())); if (scorer != null) { boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS; Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more); @@ -327,7 +327,7 @@ final IndexReader previousReader = lastReader[0]; IndexSearcher indexSearcher = LuceneTestCase.newSearcher(previousReader, false); Weight w = indexSearcher.createNormalizedWeight(q); - Scorer scorer = w.scorer((AtomicReaderContext)previousReader.getTopReaderContext(), ScorerContext.def()); + Scorer scorer = w.scorer((AtomicReaderContext)previousReader.getTopReaderContext(), ScorerContext.def().acceptOnlyDocs(previousReader.getLiveDocs())); if (scorer != null) { boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS; Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more); @@ -358,7 +358,7 @@ long startMS = System.currentTimeMillis(); for (int i=lastDoc[0]+1; i<=doc; i++) { Weight w = s.createNormalizedWeight(q); - Scorer scorer = w.scorer(context[leafPtr], ScorerContext.def()); + Scorer scorer = w.scorer(context[leafPtr], ScorerContext.def().acceptOnlyDocs(context[leafPtr].reader.getLiveDocs())); Assert.assertTrue("query collected "+doc+" but skipTo("+i+") says no more docs!",scorer.advance(i) != DocIdSetIterator.NO_MORE_DOCS); Assert.assertEquals("query collected "+doc+" but skipTo("+i+") got to "+scorer.docID(),doc,scorer.docID()); float skipToScore = scorer.score(); @@ -385,7 +385,7 @@ final IndexReader previousReader = lastReader[0]; IndexSearcher indexSearcher = LuceneTestCase.newSearcher(previousReader); Weight w = indexSearcher.createNormalizedWeight(q); - Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), ScorerContext.def()); + Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), ScorerContext.def().acceptOnlyDocs(previousReader.getLiveDocs())); if (scorer != null) { boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS; Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more); @@ -409,7 +409,7 @@ final IndexReader previousReader = lastReader[0]; IndexSearcher indexSearcher = LuceneTestCase.newSearcher(previousReader); Weight w = indexSearcher.createNormalizedWeight(q); - Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), ScorerContext.def()); + Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), ScorerContext.def().acceptOnlyDocs(previousReader.getLiveDocs())); if (scorer != null) { boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS; Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more); Index: lucene/src/test-framework/org/apache/lucene/util/LuceneTestCase.java --- lucene/src/test-framework/org/apache/lucene/util/LuceneTestCase.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/test-framework/org/apache/lucene/util/LuceneTestCase.java Sat Jul 09 10:52:11 2011 -0400 @@ -1257,6 +1257,12 @@ * with one that returns null for getSequentialSubReaders. */ public static IndexSearcher newSearcher(IndexReader r, boolean maybeWrap) throws IOException { + // nocommit fix IS wrapper to enable "low" filtering for + // search methods taking Filter + + // nocommit if r has deletions can we randomly pretend + // it does not and then pass filter to search methods + // instead? if (random.nextBoolean()) { if (maybeWrap && rarely()) { r = new SlowMultiReaderWrapper(r); Index: lucene/src/test/org/apache/lucene/search/TestConstantScoreQuery.java --- lucene/src/test/org/apache/lucene/search/TestConstantScoreQuery.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/test/org/apache/lucene/search/TestConstantScoreQuery.java Sat Jul 09 10:52:11 2011 -0400 @@ -26,6 +26,8 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.util.LuceneTestCase; +import org.junit.Ignore; + import java.io.IOException; /** This class only tests some basic functionality in CSQ, the main parts are mostly @@ -129,5 +131,34 @@ if (directory != null) directory.close(); } } + + // nocommit -- enable this test once we fixed CSQ + @Ignore + public void testConstantScoreQueryAndFilter() throws Exception { + Directory d = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random, d); + Document doc = new Document(); + doc.add(newField("field", "a", Field.Index.NOT_ANALYZED)); + w.addDocument(doc); + doc = new Document(); + doc.add(newField("field", "b", Field.Index.NOT_ANALYZED)); + w.addDocument(doc); + IndexReader r = w.getReader(); + w.close(); + + // Only matches first doc + //Query q = new ConstantScoreQuery(new TermQuery(new Term("field", "a"))); + Filter fa = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "b"))), CachingWrapperFilter.DeletesMode.RECACHE); + Query q = new ConstantScoreQuery(fa); + + // Only matches 2nd doc + Filter fb = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "b"))), CachingWrapperFilter.DeletesMode.RECACHE); + + IndexSearcher s = new IndexSearcher(r); + assertEquals(0, s.search(q, fb, 1).totalHits); + + r.close(); + d.close(); + } } Index: lucene/src/test/org/apache/lucene/search/TestNumericRangeQuery64.java --- lucene/src/test/org/apache/lucene/search/TestNumericRangeQuery64.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/test/org/apache/lucene/search/TestNumericRangeQuery64.java Sat Jul 09 10:52:11 2011 -0400 @@ -120,6 +120,9 @@ NumericRangeFilter f = NumericRangeFilter.newLongRange(field, precisionStep, lower, upper, true, true); int lastTerms = 0; for (byte i=0; i<3; i++) { + if (VERBOSE) { + System.out.println("TEST: i=" + i); + } TopDocs topDocs; int terms; String type; Index: lucene/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java --- lucene/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java Sat Jul 09 10:52:11 2011 -0400 @@ -82,7 +82,7 @@ } @Override - public Spans getSpans(AtomicReaderContext context) throws IOException { + public Spans getSpans(AtomicReaderContext context, Weight.ScorerContext scorerContext) throws IOException { throw new UnsupportedOperationException(UNSUPPORTED_MSG); } Index: lucene/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java --- lucene/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java Fri Jul 08 18:08:17 2011 -0400 +++ lucene/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java Sat Jul 09 10:52:11 2011 -0400 @@ -24,6 +24,7 @@ import org.apache.lucene.index.DocsEnum; import org.apache.lucene.index.IndexReader.AtomicReaderContext; import org.apache.lucene.index.IndexReader.ReaderContext; +import org.apache.lucene.search.Weight; import org.apache.lucene.util.ReaderUtil; /** @@ -48,8 +49,8 @@ public static Spans wrap(ReaderContext topLevelReaderContext, SpanQuery query) throws IOException { AtomicReaderContext[] leaves = ReaderUtil.leaves(topLevelReaderContext); - if(leaves.length == 1) { - return query.getSpans(leaves[0]); + if (leaves.length == 1) { + return query.getSpans(leaves[0], Weight.ScorerContext.def().acceptOnlyDocs(leaves[0].reader.getLiveDocs())); } return new MultiSpansWrapper(leaves, query); } @@ -60,14 +61,14 @@ return false; } if (current == null) { - current = query.getSpans(leaves[leafOrd]); + current = query.getSpans(leaves[leafOrd], Weight.ScorerContext.def().acceptOnlyDocs(leaves[leafOrd].reader.getLiveDocs())); } while(true) { if (current.next()) { return true; } if (++leafOrd < leaves.length) { - current = query.getSpans(leaves[leafOrd]); + current = query.getSpans(leaves[leafOrd], Weight.ScorerContext.def().acceptOnlyDocs(leaves[leafOrd].reader.getLiveDocs())); } else { current = null; break; @@ -85,17 +86,17 @@ int subIndex = ReaderUtil.subIndex(target, leaves); assert subIndex >= leafOrd; if (subIndex != leafOrd) { - current = query.getSpans(leaves[subIndex]); + current = query.getSpans(leaves[subIndex], Weight.ScorerContext.def().acceptOnlyDocs(leaves[leafOrd].reader.getLiveDocs())); leafOrd = subIndex; } else if (current == null) { - current = query.getSpans(leaves[leafOrd]); + current = query.getSpans(leaves[leafOrd], Weight.ScorerContext.def().acceptOnlyDocs(leaves[leafOrd].reader.getLiveDocs())); } while (true) { if (current.skipTo(target - leaves[leafOrd].docBase)) { return true; } if (++leafOrd < leaves.length) { - current = query.getSpans(leaves[leafOrd]); + current = query.getSpans(leaves[leafOrd], Weight.ScorerContext.def().acceptOnlyDocs(leaves[leafOrd].reader.getLiveDocs())); } else { current = null; break;