Index: lucene/core/src/java/org/apache/lucene/search/CachingWrapperFilter.java =================================================================== --- lucene/core/src/java/org/apache/lucene/search/CachingWrapperFilter.java (revision 1476105) +++ lucene/core/src/java/org/apache/lucene/search/CachingWrapperFilter.java (working copy) @@ -24,7 +24,6 @@ import org.apache.lucene.index.AtomicReader; import org.apache.lucene.index.AtomicReaderContext; -import org.apache.lucene.index.DirectoryReader; // javadocs import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.Bits; @@ -47,25 +46,28 @@ this.filter = filter; } - /** Provide the DocIdSet to be cached, using the DocIdSet provided - * by the wrapped Filter. - *
This implementation returns the given {@link DocIdSet}, if {@link DocIdSet#isCacheable}
- * returns true, else it copies the {@link DocIdSetIterator} into
- * a {@link FixedBitSet}.
+ /**
+ * Provide the DocIdSet to be cached, using the DocIdSet provided
+ * by the wrapped Filter.
This implementation returns the given {@link DocIdSet},
+ * if {@link DocIdSet#isCacheable} returns true, else it copies the
+ * {@link DocIdSetIterator} into a {@link FixedBitSet}.
+ *
Note: This method returns {@linkplain #EMPTY_DOCIDSET} if the given docIdSet
+ * is null or if {@link DocIdSet#iterator()} return null. The empty
+ * instance is use as a placeholder in the cache instead of the null value.
*/
protected DocIdSet docIdSetToCache(DocIdSet docIdSet, AtomicReader reader) throws IOException {
if (docIdSet == null) {
// this is better than returning null, as the nonnull result can be cached
- return DocIdSet.EMPTY_DOCIDSET;
+ return EMPTY_DOCIDSET;
} else if (docIdSet.isCacheable()) {
return docIdSet;
} else {
final DocIdSetIterator it = docIdSet.iterator();
// null is allowed to be returned by iterator(),
- // in this case we wrap with the empty set,
+ // in this case we wrap with the sentinel set,
// which is cacheable.
if (it == null) {
- return DocIdSet.EMPTY_DOCIDSET;
+ return EMPTY_DOCIDSET;
} else {
final FixedBitSet bits = new FixedBitSet(reader.maxDoc());
bits.or(it);
@@ -91,9 +93,9 @@
cache.put(key, docIdSet);
}
- return BitsFilteredDocIdSet.wrap(docIdSet, acceptDocs);
+ return docIdSet == EMPTY_DOCIDSET ? null : BitsFilteredDocIdSet.wrap(docIdSet, acceptDocs);
}
-
+
@Override
public String toString() {
return "CachingWrapperFilter("+filter+")";
@@ -110,4 +112,24 @@
public int hashCode() {
return (filter.hashCode() ^ 0x1117BF25);
}
+
+ /** An empty {@code DocIdSet} instance */
+ protected static final DocIdSet EMPTY_DOCIDSET = new DocIdSet() {
+
+ @Override
+ public DocIdSetIterator iterator() {
+ return DocIdSetIterator.empty();
+ }
+
+ @Override
+ public boolean isCacheable() {
+ return true;
+ }
+
+ // we explicitly provide no random access, as this filter is 100% sparse and iterator exits faster
+ @Override
+ public Bits bits() {
+ return null;
+ }
+ };
}
Index: lucene/core/src/java/org/apache/lucene/search/DocIdSet.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DocIdSet.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/DocIdSet.java (working copy)
@@ -26,56 +26,8 @@
*/
public abstract class DocIdSet {
- /** An empty {@code DocIdSet} instance for easy use, e.g. in Filters that hit no documents. */
- public static final DocIdSet EMPTY_DOCIDSET = new DocIdSet() {
-
- @Override
- public DocIdSetIterator iterator() {
- return new DocIdSetIterator() {
- boolean exhausted = false;
-
- @Override
- public int advance(int target) {
- assert !exhausted;
- assert target >= 0;
- exhausted = true;
- return NO_MORE_DOCS;
- }
-
- @Override
- public int docID() {
- return exhausted ? NO_MORE_DOCS : -1;
- }
-
- @Override
- public int nextDoc() {
- assert !exhausted;
- exhausted = true;
- return NO_MORE_DOCS;
- }
-
- @Override
- public long cost() {
- return 0;
- }
- };
- }
-
- @Override
- public boolean isCacheable() {
- return true;
- }
-
- // we explicitely provide no random access, as this filter is 100% sparse and iterator exits faster
- @Override
- public Bits bits() {
- return null;
- }
- };
-
/** Provides a {@link DocIdSetIterator} to access the set.
- * This implementation can return null or
- * {@linkplain #EMPTY_DOCIDSET}.iterator() if there
+ * This implementation can return null if there
* are no docs that match. */
public abstract DocIdSetIterator iterator() throws IOException;
Index: lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java (working copy)
@@ -28,6 +28,37 @@
*/
public abstract class DocIdSetIterator {
+ /** An empty {@code DocIdSetIterator} instance */
+ public static final DocIdSetIterator empty() {
+ return new DocIdSetIterator() {
+ boolean exhausted = false;
+
+ @Override
+ public int advance(int target) {
+ assert !exhausted;
+ assert target >= 0;
+ exhausted = true;
+ return NO_MORE_DOCS;
+ }
+
+ @Override
+ public int docID() {
+ return exhausted ? NO_MORE_DOCS : -1;
+ }
+ @Override
+ public int nextDoc() {
+ assert !exhausted;
+ exhausted = true;
+ return NO_MORE_DOCS;
+ }
+
+ @Override
+ public long cost() {
+ return 0;
+ }
+ };
+ }
+
/**
* When returned by {@link #nextDoc()}, {@link #advance(int)} and
* {@link #docID()} it means there are no more docs in the iterator.
Index: lucene/core/src/java/org/apache/lucene/search/DocTermOrdsRangeFilter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DocTermOrdsRangeFilter.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/DocTermOrdsRangeFilter.java (working copy)
@@ -90,7 +90,7 @@
}
if (inclusiveUpperPoint < 0 || inclusiveLowerPoint > inclusiveUpperPoint) {
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
}
assert inclusiveLowerPoint >= 0 && inclusiveUpperPoint >= 0;
Index: lucene/core/src/java/org/apache/lucene/search/DocTermOrdsRewriteMethod.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DocTermOrdsRewriteMethod.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/DocTermOrdsRewriteMethod.java (working copy)
@@ -143,7 +143,7 @@
termSet.set(termsEnum.ord());
} while (termsEnum.next() != null);
} else {
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
}
return new FieldCacheDocIdSet(context.reader().maxDoc(), acceptDocs) {
Index: lucene/core/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java (working copy)
@@ -121,7 +121,7 @@
}
if (inclusiveUpperPoint < 0 || inclusiveLowerPoint > inclusiveUpperPoint) {
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
}
assert inclusiveLowerPoint >= 0 && inclusiveUpperPoint >= 0;
@@ -178,7 +178,7 @@
}
if (inclusiveUpperPoint < 0 || inclusiveLowerPoint > inclusiveUpperPoint) {
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
}
assert inclusiveLowerPoint >= 0 && inclusiveUpperPoint >= 0;
@@ -216,7 +216,7 @@
if (lowerVal != null) {
final byte i = lowerVal.byteValue();
if (!includeLower && i == Byte.MAX_VALUE)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
inclusiveLowerPoint = (byte) (includeLower ? i : (i + 1));
} else {
inclusiveLowerPoint = Byte.MIN_VALUE;
@@ -224,14 +224,14 @@
if (upperVal != null) {
final byte i = upperVal.byteValue();
if (!includeUpper && i == Byte.MIN_VALUE)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
inclusiveUpperPoint = (byte) (includeUpper ? i : (i - 1));
} else {
inclusiveUpperPoint = Byte.MAX_VALUE;
}
if (inclusiveLowerPoint > inclusiveUpperPoint)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
final FieldCache.Bytes values = FieldCache.DEFAULT.getBytes(context.reader(), field, (FieldCache.ByteParser) parser, false);
return new FieldCacheDocIdSet(context.reader().maxDoc(), acceptDocs) {
@@ -267,7 +267,7 @@
if (lowerVal != null) {
short i = lowerVal.shortValue();
if (!includeLower && i == Short.MAX_VALUE)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
inclusiveLowerPoint = (short) (includeLower ? i : (i + 1));
} else {
inclusiveLowerPoint = Short.MIN_VALUE;
@@ -275,14 +275,14 @@
if (upperVal != null) {
short i = upperVal.shortValue();
if (!includeUpper && i == Short.MIN_VALUE)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
inclusiveUpperPoint = (short) (includeUpper ? i : (i - 1));
} else {
inclusiveUpperPoint = Short.MAX_VALUE;
}
if (inclusiveLowerPoint > inclusiveUpperPoint)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
final FieldCache.Shorts values = FieldCache.DEFAULT.getShorts(context.reader(), field, (FieldCache.ShortParser) parser, false);
return new FieldCacheDocIdSet(context.reader().maxDoc(), acceptDocs) {
@@ -318,7 +318,7 @@
if (lowerVal != null) {
int i = lowerVal.intValue();
if (!includeLower && i == Integer.MAX_VALUE)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
inclusiveLowerPoint = includeLower ? i : (i + 1);
} else {
inclusiveLowerPoint = Integer.MIN_VALUE;
@@ -326,14 +326,14 @@
if (upperVal != null) {
int i = upperVal.intValue();
if (!includeUpper && i == Integer.MIN_VALUE)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
inclusiveUpperPoint = includeUpper ? i : (i - 1);
} else {
inclusiveUpperPoint = Integer.MAX_VALUE;
}
if (inclusiveLowerPoint > inclusiveUpperPoint)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
final FieldCache.Ints values = FieldCache.DEFAULT.getInts(context.reader(), field, (FieldCache.IntParser) parser, false);
return new FieldCacheDocIdSet(context.reader().maxDoc(), acceptDocs) {
@@ -369,7 +369,7 @@
if (lowerVal != null) {
long i = lowerVal.longValue();
if (!includeLower && i == Long.MAX_VALUE)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
inclusiveLowerPoint = includeLower ? i : (i + 1L);
} else {
inclusiveLowerPoint = Long.MIN_VALUE;
@@ -377,14 +377,14 @@
if (upperVal != null) {
long i = upperVal.longValue();
if (!includeUpper && i == Long.MIN_VALUE)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
inclusiveUpperPoint = includeUpper ? i : (i - 1L);
} else {
inclusiveUpperPoint = Long.MAX_VALUE;
}
if (inclusiveLowerPoint > inclusiveUpperPoint)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
final FieldCache.Longs values = FieldCache.DEFAULT.getLongs(context.reader(), field, (FieldCache.LongParser) parser, false);
return new FieldCacheDocIdSet(context.reader().maxDoc(), acceptDocs) {
@@ -422,7 +422,7 @@
if (lowerVal != null) {
float f = lowerVal.floatValue();
if (!includeUpper && f > 0.0f && Float.isInfinite(f))
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
int i = NumericUtils.floatToSortableInt(f);
inclusiveLowerPoint = NumericUtils.sortableIntToFloat( includeLower ? i : (i + 1) );
} else {
@@ -431,7 +431,7 @@
if (upperVal != null) {
float f = upperVal.floatValue();
if (!includeUpper && f < 0.0f && Float.isInfinite(f))
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
int i = NumericUtils.floatToSortableInt(f);
inclusiveUpperPoint = NumericUtils.sortableIntToFloat( includeUpper ? i : (i - 1) );
} else {
@@ -439,7 +439,7 @@
}
if (inclusiveLowerPoint > inclusiveUpperPoint)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
final FieldCache.Floats values = FieldCache.DEFAULT.getFloats(context.reader(), field, (FieldCache.FloatParser) parser, false);
return new FieldCacheDocIdSet(context.reader().maxDoc(), acceptDocs) {
@@ -477,7 +477,7 @@
if (lowerVal != null) {
double f = lowerVal.doubleValue();
if (!includeUpper && f > 0.0 && Double.isInfinite(f))
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
long i = NumericUtils.doubleToSortableLong(f);
inclusiveLowerPoint = NumericUtils.sortableLongToDouble( includeLower ? i : (i + 1L) );
} else {
@@ -486,7 +486,7 @@
if (upperVal != null) {
double f = upperVal.doubleValue();
if (!includeUpper && f < 0.0 && Double.isInfinite(f))
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
long i = NumericUtils.doubleToSortableLong(f);
inclusiveUpperPoint = NumericUtils.sortableLongToDouble( includeUpper ? i : (i - 1L) );
} else {
@@ -494,7 +494,7 @@
}
if (inclusiveLowerPoint > inclusiveUpperPoint)
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
final FieldCache.Doubles values = FieldCache.DEFAULT.getDoubles(context.reader(), field, (FieldCache.DoubleParser) parser, false);
// ignore deleted docs if range doesn't contain 0
Index: lucene/core/src/java/org/apache/lucene/search/FieldCacheRewriteMethod.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FieldCacheRewriteMethod.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/FieldCacheRewriteMethod.java (working copy)
@@ -146,7 +146,7 @@
}
} while (termsEnum.next() != null);
} else {
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
}
return new FieldCacheDocIdSet(context.reader().maxDoc(), acceptDocs) {
Index: lucene/core/src/java/org/apache/lucene/search/Filter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/Filter.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/Filter.java (working copy)
@@ -21,7 +21,6 @@
import org.apache.lucene.index.AtomicReader; // javadocs
import org.apache.lucene.index.AtomicReaderContext;
-import org.apache.lucene.index.IndexReader; // javadocs
import org.apache.lucene.util.Bits;
/**
@@ -53,8 +52,9 @@
* but possibly filtering other documents)
*
* @return a DocIdSet that provides the documents which should be permitted or
- * prohibited in search results. NOTE: null can be returned if
- * no documents will be accepted by this Filter.
+ * prohibited in search results. NOTE: null should be returned if
+ * the filter doesn't accept any documents otherwise internal optimization might not apply
+ * in the case an empty {@link DocIdSet} is returned.
*/
public abstract DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException;
}
Index: lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java (working copy)
@@ -102,9 +102,9 @@
Explanation inner = weight.explain (ir, i);
Filter f = FilteredQuery.this.filter;
DocIdSet docIdSet = f.getDocIdSet(ir, ir.reader().getLiveDocs());
- DocIdSetIterator docIdSetIterator = docIdSet == null ? DocIdSet.EMPTY_DOCIDSET.iterator() : docIdSet.iterator();
+ DocIdSetIterator docIdSetIterator = docIdSet == null ? DocIdSetIterator.empty() : docIdSet.iterator();
if (docIdSetIterator == null) {
- docIdSetIterator = DocIdSet.EMPTY_DOCIDSET.iterator();
+ docIdSetIterator = DocIdSetIterator.empty();
}
if (docIdSetIterator.advance(i) == i) {
return inner;
Index: lucene/core/src/java/org/apache/lucene/search/MultiTermQueryWrapperFilter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/MultiTermQueryWrapperFilter.java (revision 1476105)
+++ lucene/core/src/java/org/apache/lucene/search/MultiTermQueryWrapperFilter.java (working copy)
@@ -88,13 +88,13 @@
final Fields fields = reader.fields();
if (fields == null) {
// reader has no fields
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
}
final Terms terms = fields.terms(query.field);
if (terms == null) {
// field does not exist
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
}
final TermsEnum termsEnum = query.getTermsEnum(terms);
@@ -116,7 +116,7 @@
return bitSet;
} else {
- return DocIdSet.EMPTY_DOCIDSET;
+ return null;
}
}
}
Index: lucene/core/src/test/org/apache/lucene/search/TestCachingWrapperFilter.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestCachingWrapperFilter.java (revision 1476105)
+++ lucene/core/src/test/org/apache/lucene/search/TestCachingWrapperFilter.java (working copy)
@@ -80,7 +80,7 @@
CachingWrapperFilter cacher = new CachingWrapperFilter(filter);
// the caching filter should return the empty set constant
- assertSame(DocIdSet.EMPTY_DOCIDSET, cacher.getDocIdSet(context, context.reader().getLiveDocs()));
+ assertNull(cacher.getDocIdSet(context, context.reader().getLiveDocs()));
reader.close();
dir.close();
@@ -108,7 +108,7 @@
CachingWrapperFilter cacher = new CachingWrapperFilter(filter);
// the caching filter should return the empty set constant
- assertSame(DocIdSet.EMPTY_DOCIDSET, cacher.getDocIdSet(context, context.reader().getLiveDocs()));
+ assertNull(cacher.getDocIdSet(context, context.reader().getLiveDocs()));
reader.close();
dir.close();
@@ -120,13 +120,20 @@
final CachingWrapperFilter cacher = new CachingWrapperFilter(filter);
final DocIdSet originalSet = filter.getDocIdSet(context, context.reader().getLiveDocs());
final DocIdSet cachedSet = cacher.getDocIdSet(context, context.reader().getLiveDocs());
- assertTrue(cachedSet.isCacheable());
- assertEquals(shouldCacheable, originalSet.isCacheable());
- //System.out.println("Original: "+originalSet.getClass().getName()+" -- cached: "+cachedSet.getClass().getName());
- if (originalSet.isCacheable()) {
- assertEquals("Cached DocIdSet must be of same class like uncached, if cacheable", originalSet.getClass(), cachedSet.getClass());
+ if (originalSet == null) {
+ assertNull(cachedSet);
+ }
+ if (cachedSet == null) {
+ assertTrue(originalSet == null || originalSet.iterator() == null);
} else {
- assertTrue("Cached DocIdSet must be an FixedBitSet if the original one was not cacheable", cachedSet instanceof FixedBitSet || cachedSet == DocIdSet.EMPTY_DOCIDSET);
+ assertTrue(cachedSet.isCacheable());
+ assertEquals(shouldCacheable, originalSet.isCacheable());
+ //System.out.println("Original: "+originalSet.getClass().getName()+" -- cached: "+cachedSet.getClass().getName());
+ if (originalSet.isCacheable()) {
+ assertEquals("Cached DocIdSet must be of same class like uncached, if cacheable", originalSet.getClass(), cachedSet.getClass());
+ } else {
+ assertTrue("Cached DocIdSet must be an FixedBitSet if the original one was not cacheable", cachedSet instanceof FixedBitSet || cachedSet == null);
+ }
}
}
Index: lucene/core/src/test/org/apache/lucene/search/TestNumericRangeQuery32.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestNumericRangeQuery32.java (revision 1476105)
+++ lucene/core/src/test/org/apache/lucene/search/TestNumericRangeQuery32.java (working copy)
@@ -200,13 +200,11 @@
public void testInverseRange() throws Exception {
AtomicReaderContext context = SlowCompositeReaderWrapper.wrap(reader).getContext();
NumericRangeFilter