Index: src/java/org/apache/lucene/search/FieldCacheRangeFilter.java =================================================================== --- src/java/org/apache/lucene/search/FieldCacheRangeFilter.java (revision 789589) +++ src/java/org/apache/lucene/search/FieldCacheRangeFilter.java (working copy) @@ -19,74 +19,430 @@ import java.io.IOException; import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.TermDocs; +import org.apache.lucene.util.NumericUtils; +import org.apache.lucene.document.NumericField; // for javadocs /** - * A range filter built on top of a cached single term field (in FieldCache). + * A range filter built on top of a cached single term field (in {@link FieldCache}). * - * FieldCacheRangeFilter builds a single cache for the field the first time it is used. - * + *
FieldCacheRangeFilter builds a single cache for the field the first time it is used. * Each subsequent FieldCacheRangeFilter on the same field then reuses this cache, * even if the range itself changes. * - * This means that FieldCacheRangeFilter is much faster (sometimes more than 100x as fast) - * as building a RangeFilter (or ConstantScoreRangeQuery on a RangeFilter) for each query. - * However, if the range never changes it is slower (around 2x as slow) than building a - * CachingWrapperFilter on top of a single RangeFilter. - * - * As with all FieldCache based functionality, FieldCacheRangeFilter is only valid for - * fields which contain zero or one terms for each document. Thus it works on dates, - * prices and other single value fields but will not work on regular text fields. It is - * preferable to use an UN_TOKENIZED field to ensure that there is only a single term. + *
This means that FieldCacheRangeFilter is much faster (sometimes more than 100x as fast) + * as building a {@link RangeFilter} (or {@link ConstantScoreRangeQuery} on a {@link RangeFilter}) + * for each query, if using a {@link #newStringRange}. However, if the range never changes it + * is slower (around 2x as slow) than building a CachingWrapperFilter on top of a single RangeFilter. * - * Also, collation is done at the time the FieldCache is built; to change - * collation you need to override the getFieldCache() method to change the underlying cache. + * For numeric data types, this filter may be significantly faster than {@link NumericRangeFilter}. + * Furthermore, it does not need the numeric values encoded by {@link NumericField}. But + * it has the problem that it only works with exact one value/document (see below). + * + *
As with all {@link FieldCache} based functionality, FieldCacheRangeFilter is only valid for + * fields which exact one term for each document (except for {@link #newStringRange} + * where 0 terms are also allowed). Due to a restriction of {@link FieldCache}, for numeric ranges + * all terms that do not have a numeric value, 0 is assumed. + * + *
Thus it works on dates, prices and other single value fields but will not work on
+ * regular text fields. It is preferable to use a NOT_ANALYZED field to ensure that
+ * there is only a single term.
+ *
+ *
This class does not have an constructor, use one of the static factory methods available,
+ * that create a correct instance for different data types supported by {@link FieldCache}.
*/
-public class FieldCacheRangeFilter extends Filter {
- private String field;
- private String lowerVal;
- private String upperVal;
- private boolean includeLower;
- private boolean includeUpper;
+public abstract class FieldCacheRangeFilter extends Filter {
+ final String field;
+ final FieldCache.Parser parser;
+ final Object lowerVal;
+ final Object upperVal;
+ final boolean includeLower;
+ final boolean includeUpper;
- public FieldCacheRangeFilter(
- String field,
- String lowerVal,
- String upperVal,
- boolean includeLower,
- boolean includeUpper) {
+ private FieldCacheRangeFilter(String field, FieldCache.Parser parser, Object lowerVal, Object upperVal, boolean includeLower, boolean includeUpper) {
+ if (lowerVal == null && upperVal == null)
+ throw new IllegalArgumentException("At least one value must be non-null");
this.field = field;
+ this.parser = parser;
this.lowerVal = lowerVal;
this.upperVal = upperVal;
this.includeLower = includeLower;
this.includeUpper = includeUpper;
}
+
+ /** This method is implemented for each data type */
+ public abstract DocIdSet getDocIdSet(IndexReader reader) throws IOException;
- public FieldCache getFieldCache() {
- return FieldCache.DEFAULT;
+ /**
+ * Creates a string range query using {@link FieldCache#getStringIndex}. This works with all
+ * fields containing zero or one term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newStringRange(String field, String lowerVal, String upperVal, boolean includeLower, boolean includeUpper) {
+ return new FieldCacheRangeFilter(field, null, lowerVal, upperVal, includeLower, includeUpper) {
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ final FieldCache.StringIndex fcsi = FieldCache.DEFAULT.getStringIndex(reader, field);
+ final int lowerPoint = fcsi.binarySearchLookup((String) lowerVal);
+ final int upperPoint = fcsi.binarySearchLookup((String) upperVal);
+
+ final int inclusiveLowerPoint, inclusiveUpperPoint;
+
+ // Hints:
+ // * binarySearchLookup returns 0, if value was null.
+ // * the value is <0 if no exact hit was found, the returned value
+ // is (-(insertion point) - 1)
+ if (lowerPoint == 0) {
+ assert lowerVal == null;
+ inclusiveLowerPoint = 1;
+ } else if (includeLower && lowerPoint > 0) {
+ inclusiveLowerPoint = lowerPoint;
+ } else if (lowerPoint > 0) {
+ inclusiveLowerPoint = lowerPoint + 1;
+ } else {
+ inclusiveLowerPoint = Math.max(1, -lowerPoint - 1);
+ }
+
+ if (upperPoint == 0) {
+ assert upperVal == null;
+ inclusiveUpperPoint = Integer.MAX_VALUE;
+ } else if (includeUpper && upperPoint > 0) {
+ inclusiveUpperPoint = upperPoint;
+ } else if (upperPoint > 0) {
+ inclusiveUpperPoint = upperPoint - 1;
+ } else {
+ inclusiveUpperPoint = -upperPoint - 2;
+ }
+
+ if (inclusiveUpperPoint <= 0 || inclusiveLowerPoint > inclusiveUpperPoint)
+ return DocIdSet.EMPTY_DOCIDSET;
+
+ assert inclusiveLowerPoint > 0 && inclusiveUpperPoint > 0;
+
+ // for this DocIdSet, we never need to use TermDocs,
+ // because deleted docs have an order of 0 (null entry in StringIndex)
+ return new FieldCacheDocIdSet(reader, false) {
+ final boolean matchDoc(int doc) {
+ return fcsi.order[doc] >= inclusiveLowerPoint && fcsi.order[doc] <= inclusiveUpperPoint;
+ }
+ };
+ }
+ };
}
- public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
- return new RangeMultiFilterDocIdSet(getFieldCache().getStringIndex(reader, field));
+ /**
+ * Creates a numeric range query using {@link FieldCache#getBytes(IndexReader,String)}. This works with all
+ * byte fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newByteRange(String field, Byte lowerVal, Byte upperVal, boolean includeLower, boolean includeUpper) {
+ return newByteRange(field, null, lowerVal, upperVal, includeLower, includeUpper);
}
- public String toString() {
- StringBuffer buffer = new StringBuffer();
- buffer.append(field);
- buffer.append(":");
- buffer.append(includeLower ? "[" : "{");
- if (null != lowerVal) {
- buffer.append(lowerVal);
- }
- buffer.append("-");
- if (null != upperVal) {
- buffer.append(upperVal);
- }
- buffer.append(includeUpper ? "]" : "}");
- return buffer.toString();
+ /**
+ * Creates a numeric range query using {@link FieldCache#getBytes(IndexReader,String,FieldCache.ByteParser)}. This works with all
+ * byte fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newByteRange(String field, FieldCache.ByteParser parser, Byte lowerVal, Byte upperVal, boolean includeLower, boolean includeUpper) {
+ return new FieldCacheRangeFilter(field, parser, lowerVal, upperVal, includeLower, includeUpper) {
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ final byte inclusiveLowerPoint, inclusiveUpperPoint;
+ if (lowerVal != null) {
+ final byte i = ((Number) lowerVal).byteValue();
+ if (!includeLower && i == Byte.MAX_VALUE)
+ return DocIdSet.EMPTY_DOCIDSET;
+ inclusiveLowerPoint = (byte) (includeLower ? i : (i + 1));
+ } else {
+ inclusiveLowerPoint = Byte.MIN_VALUE;
+ }
+ if (upperVal != null) {
+ final byte i = ((Number) upperVal).byteValue();
+ if (!includeUpper && i == Byte.MIN_VALUE)
+ return DocIdSet.EMPTY_DOCIDSET;
+ inclusiveUpperPoint = (byte) (includeUpper ? i : (i - 1));
+ } else {
+ inclusiveUpperPoint = Byte.MAX_VALUE;
+ }
+
+ if (inclusiveLowerPoint > inclusiveUpperPoint)
+ return DocIdSet.EMPTY_DOCIDSET;
+
+ final byte[] values = FieldCache.DEFAULT.getBytes(reader, field, (FieldCache.ByteParser) parser);
+ // we only request the usage of termDocs, if the range contains 0
+ return new FieldCacheDocIdSet(reader, (inclusiveLowerPoint <= 0 && inclusiveUpperPoint >= 0)) {
+ boolean matchDoc(int doc) {
+ return values[doc] >= inclusiveLowerPoint && values[doc] <= inclusiveUpperPoint;
+ }
+ };
+ }
+ };
}
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getShorts(IndexReader,String)}. This works with all
+ * short fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newShortRange(String field, Short lowerVal, Short upperVal, boolean includeLower, boolean includeUpper) {
+ return newShortRange(field, null, lowerVal, upperVal, includeLower, includeUpper);
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getShorts(IndexReader,String,FieldCache.ShortParser)}. This works with all
+ * short fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newShortRange(String field, FieldCache.ShortParser parser, Short lowerVal, Short upperVal, boolean includeLower, boolean includeUpper) {
+ return new FieldCacheRangeFilter(field, parser, lowerVal, upperVal, includeLower, includeUpper) {
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ final short inclusiveLowerPoint, inclusiveUpperPoint;
+ if (lowerVal != null) {
+ short i = ((Number) lowerVal).shortValue();
+ if (!includeLower && i == Short.MAX_VALUE)
+ return DocIdSet.EMPTY_DOCIDSET;
+ inclusiveLowerPoint = (short) (includeLower ? i : (i + 1));
+ } else {
+ inclusiveLowerPoint = Short.MIN_VALUE;
+ }
+ if (upperVal != null) {
+ short i = ((Number) upperVal).shortValue();
+ if (!includeUpper && i == Short.MIN_VALUE)
+ return DocIdSet.EMPTY_DOCIDSET;
+ inclusiveUpperPoint = (short) (includeUpper ? i : (i - 1));
+ } else {
+ inclusiveUpperPoint = Short.MAX_VALUE;
+ }
+
+ if (inclusiveLowerPoint > inclusiveUpperPoint)
+ return DocIdSet.EMPTY_DOCIDSET;
+
+ final short[] values = FieldCache.DEFAULT.getShorts(reader, field, (FieldCache.ShortParser) parser);
+ // we only request the usage of termDocs, if the range contains 0
+ return new FieldCacheDocIdSet(reader, (inclusiveLowerPoint <= 0 && inclusiveUpperPoint >= 0)) {
+ boolean matchDoc(int doc) {
+ return values[doc] >= inclusiveLowerPoint && values[doc] <= inclusiveUpperPoint;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getInts(IndexReader,String)}. This works with all
+ * int fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newIntRange(String field, Integer lowerVal, Integer upperVal, boolean includeLower, boolean includeUpper) {
+ return newIntRange(field, null, lowerVal, upperVal, includeLower, includeUpper);
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getInts(IndexReader,String,FieldCache.IntParser)}. This works with all
+ * int fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newIntRange(String field, FieldCache.IntParser parser, Integer lowerVal, Integer upperVal, boolean includeLower, boolean includeUpper) {
+ return new FieldCacheRangeFilter(field, parser, lowerVal, upperVal, includeLower, includeUpper) {
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ final int inclusiveLowerPoint, inclusiveUpperPoint;
+ if (lowerVal != null) {
+ int i = ((Number) lowerVal).intValue();
+ if (!includeLower && i == Integer.MAX_VALUE)
+ return DocIdSet.EMPTY_DOCIDSET;
+ inclusiveLowerPoint = includeLower ? i : (i + 1);
+ } else {
+ inclusiveLowerPoint = Integer.MIN_VALUE;
+ }
+ if (upperVal != null) {
+ int i = ((Number) upperVal).intValue();
+ if (!includeUpper && i == Integer.MIN_VALUE)
+ return DocIdSet.EMPTY_DOCIDSET;
+ inclusiveUpperPoint = includeUpper ? i : (i - 1);
+ } else {
+ inclusiveUpperPoint = Integer.MAX_VALUE;
+ }
+
+ if (inclusiveLowerPoint > inclusiveUpperPoint)
+ return DocIdSet.EMPTY_DOCIDSET;
+
+ final int[] values = FieldCache.DEFAULT.getInts(reader, field, (FieldCache.IntParser) parser);
+ // we only request the usage of termDocs, if the range contains 0
+ return new FieldCacheDocIdSet(reader, (inclusiveLowerPoint <= 0 && inclusiveUpperPoint >= 0)) {
+ boolean matchDoc(int doc) {
+ return values[doc] >= inclusiveLowerPoint && values[doc] <= inclusiveUpperPoint;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getLongs(IndexReader,String)}. This works with all
+ * long fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newLongRange(String field, Long lowerVal, Long upperVal, boolean includeLower, boolean includeUpper) {
+ return newLongRange(field, null, lowerVal, upperVal, includeLower, includeUpper);
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getLongs(IndexReader,String,FieldCache.LongParser)}. This works with all
+ * long fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newLongRange(String field, FieldCache.LongParser parser, Long lowerVal, Long upperVal, boolean includeLower, boolean includeUpper) {
+ return new FieldCacheRangeFilter(field, parser, lowerVal, upperVal, includeLower, includeUpper) {
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ final long inclusiveLowerPoint, inclusiveUpperPoint;
+ if (lowerVal != null) {
+ long i = ((Number) lowerVal).longValue();
+ if (!includeLower && i == Long.MAX_VALUE)
+ return DocIdSet.EMPTY_DOCIDSET;
+ inclusiveLowerPoint = includeLower ? i : (i + 1L);
+ } else {
+ inclusiveLowerPoint = Long.MIN_VALUE;
+ }
+ if (upperVal != null) {
+ long i = ((Number) upperVal).longValue();
+ if (!includeUpper && i == Long.MIN_VALUE)
+ return DocIdSet.EMPTY_DOCIDSET;
+ inclusiveUpperPoint = includeUpper ? i : (i - 1L);
+ } else {
+ inclusiveUpperPoint = Long.MAX_VALUE;
+ }
+
+ if (inclusiveLowerPoint > inclusiveUpperPoint)
+ return DocIdSet.EMPTY_DOCIDSET;
+
+ final long[] values = FieldCache.DEFAULT.getLongs(reader, field, (FieldCache.LongParser) parser);
+ // we only request the usage of termDocs, if the range contains 0
+ return new FieldCacheDocIdSet(reader, (inclusiveLowerPoint <= 0L && inclusiveUpperPoint >= 0L)) {
+ boolean matchDoc(int doc) {
+ return values[doc] >= inclusiveLowerPoint && values[doc] <= inclusiveUpperPoint;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getFloats(IndexReader,String)}. This works with all
+ * float fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newFloatRange(String field, Float lowerVal, Float upperVal, boolean includeLower, boolean includeUpper) {
+ return newFloatRange(field, null, lowerVal, upperVal, includeLower, includeUpper);
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getFloats(IndexReader,String,FieldCache.FloatParser)}. This works with all
+ * float fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newFloatRange(String field, FieldCache.FloatParser parser, Float lowerVal, Float upperVal, boolean includeLower, boolean includeUpper) {
+ return new FieldCacheRangeFilter(field, parser, lowerVal, upperVal, includeLower, includeUpper) {
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ // we transform the floating point numbers to sortable integers
+ // using NumericUtils to easier find the next bigger/lower value
+ final float inclusiveLowerPoint, inclusiveUpperPoint;
+ if (lowerVal != null) {
+ float f = ((Number) lowerVal).floatValue();
+ if (!includeUpper && f > 0.0f && Float.isInfinite(f))
+ return DocIdSet.EMPTY_DOCIDSET;
+ int i = NumericUtils.floatToSortableInt(f);
+ inclusiveLowerPoint = NumericUtils.sortableIntToFloat( includeLower ? i : (i + 1) );
+ } else {
+ inclusiveLowerPoint = Float.NEGATIVE_INFINITY;
+ }
+ if (upperVal != null) {
+ float f = ((Number) upperVal).floatValue();
+ if (!includeUpper && f < 0.0f && Float.isInfinite(f))
+ return DocIdSet.EMPTY_DOCIDSET;
+ int i = NumericUtils.floatToSortableInt(f);
+ inclusiveUpperPoint = NumericUtils.sortableIntToFloat( includeUpper ? i : (i - 1) );
+ } else {
+ inclusiveUpperPoint = Float.POSITIVE_INFINITY;
+ }
+
+ if (inclusiveLowerPoint > inclusiveUpperPoint)
+ return DocIdSet.EMPTY_DOCIDSET;
+
+ final float[] values = FieldCache.DEFAULT.getFloats(reader, field, (FieldCache.FloatParser) parser);
+ // we only request the usage of termDocs, if the range contains 0
+ return new FieldCacheDocIdSet(reader, (inclusiveLowerPoint <= 0.0f && inclusiveUpperPoint >= 0.0f)) {
+ boolean matchDoc(int doc) {
+ return values[doc] >= inclusiveLowerPoint && values[doc] <= inclusiveUpperPoint;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getDoubles(IndexReader,String)}. This works with all
+ * double fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newDoubleRange(String field, Double lowerVal, Double upperVal, boolean includeLower, boolean includeUpper) {
+ return newDoubleRange(field, null, lowerVal, upperVal, includeLower, includeUpper);
+ }
+
+ /**
+ * Creates a numeric range query using {@link FieldCache#getDoubles(IndexReader,String,FieldCache.DoubleParser)}. This works with all
+ * double fields containing exactly one numeric term in the field. The range can be half-open by setting one
+ * of the values to null.
+ */
+ public static FieldCacheRangeFilter newDoubleRange(String field, FieldCache.DoubleParser parser, Double lowerVal, Double upperVal, boolean includeLower, boolean includeUpper) {
+ return new FieldCacheRangeFilter(field, parser, lowerVal, upperVal, includeLower, includeUpper) {
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ // we transform the floating point numbers to sortable integers
+ // using NumericUtils to easier find the next bigger/lower value
+ final double inclusiveLowerPoint, inclusiveUpperPoint;
+ if (lowerVal != null) {
+ double f = ((Number) lowerVal).doubleValue();
+ if (!includeUpper && f > 0.0 && Double.isInfinite(f))
+ return DocIdSet.EMPTY_DOCIDSET;
+ long i = NumericUtils.doubleToSortableLong(f);
+ inclusiveLowerPoint = NumericUtils.sortableLongToDouble( includeLower ? i : (i + 1L) );
+ } else {
+ inclusiveLowerPoint = Double.NEGATIVE_INFINITY;
+ }
+ if (upperVal != null) {
+ double f = ((Number) upperVal).doubleValue();
+ if (!includeUpper && f < 0.0 && Double.isInfinite(f))
+ return DocIdSet.EMPTY_DOCIDSET;
+ long i = NumericUtils.doubleToSortableLong(f);
+ inclusiveUpperPoint = NumericUtils.sortableLongToDouble( includeUpper ? i : (i - 1L) );
+ } else {
+ inclusiveUpperPoint = Double.POSITIVE_INFINITY;
+ }
+
+ if (inclusiveLowerPoint > inclusiveUpperPoint)
+ return DocIdSet.EMPTY_DOCIDSET;
+
+ final double[] values = FieldCache.DEFAULT.getDoubles(reader, field, (FieldCache.DoubleParser) parser);
+ // we only request the usage of termDocs, if the range contains 0
+ return new FieldCacheDocIdSet(reader, (inclusiveLowerPoint <= 0.0 && inclusiveUpperPoint >= 0.0)) {
+ boolean matchDoc(int doc) {
+ return values[doc] >= inclusiveLowerPoint && values[doc] <= inclusiveUpperPoint;
+ }
+ };
+ }
+ };
+ }
+
+ public final String toString() {
+ final StringBuffer sb = new StringBuffer(field).append(":");
+ return sb.append(includeLower ? '[' : '{')
+ .append((lowerVal == null) ? "*" : lowerVal.toString())
+ .append(" TO ")
+ .append((upperVal == null) ? "*" : upperVal.toString())
+ .append(includeUpper ? ']' : '}')
+ .toString();
+ }
- public boolean equals(Object o) {
+ public final boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FieldCacheRangeFilter)) return false;
FieldCacheRangeFilter other = (FieldCacheRangeFilter) o;
@@ -97,112 +453,133 @@
) { return false; }
if (this.lowerVal != null ? !this.lowerVal.equals(other.lowerVal) : other.lowerVal != null) return false;
if (this.upperVal != null ? !this.upperVal.equals(other.upperVal) : other.upperVal != null) return false;
+ if (this.parser != null ? !this.parser.equals(other.parser) : other.parser != null) return false;
return true;
}
- public int hashCode() {
+ public final int hashCode() {
int h = field.hashCode();
- h ^= lowerVal != null ? lowerVal.hashCode() : 550356204;
+ h ^= (lowerVal != null) ? lowerVal.hashCode() : 550356204;
h = (h << 1) | (h >>> 31); // rotate to distinguish lower from upper
- h ^= (upperVal != null ? (upperVal.hashCode()) : -1674416163);
- h ^= (includeLower ? 1549299360 : -365038026)
- ^ (includeUpper ? 1721088258 : 1948649653);
-
+ h ^= (upperVal != null) ? upperVal.hashCode() : -1674416163;
+ h ^= (parser != null) ? parser.hashCode() : -1572457324;
+ h ^= (includeLower ? 1549299360 : -365038026) ^ (includeUpper ? 1721088258 : 1948649653);
return h;
}
-
- protected class RangeMultiFilterDocIdSet extends DocIdSet {
- private int inclusiveLowerPoint;
- private int inclusiveUpperPoint;
- private FieldCache.StringIndex fcsi;
-
- public RangeMultiFilterDocIdSet(FieldCache.StringIndex fcsi) {
- this.fcsi = fcsi;
- initialize();
+
+ static abstract class FieldCacheDocIdSet extends DocIdSet {
+ private final IndexReader reader;
+ private boolean mayUseTermDocs;
+
+ FieldCacheDocIdSet(IndexReader reader, boolean mayUseTermDocs) {
+ this.reader = reader;
+ this.mayUseTermDocs = mayUseTermDocs;
}
-
- private void initialize() {
- int lowerPoint = fcsi.binarySearchLookup(lowerVal);
- int upperPoint = fcsi.binarySearchLookup(upperVal);
+
+ /** this method checks, if a doc is a hit, should throw AIOBE, when position invalid */
+ abstract boolean matchDoc(int doc) throws ArrayIndexOutOfBoundsException;
- if (lowerPoint == 0 && upperPoint == 0) {
- throw new IllegalArgumentException("At least one value must be non-null");
+ public DocIdSetIterator iterator() throws IOException {
+ // Synchronization needed because deleted docs BitVector
+ // can change after call to hasDeletions until TermDocs creation.
+ // We only use an iterator with termDocs, when this was requested (e.g. range contains 0)
+ // and the index has deletions
+ final TermDocs termDocs;
+ synchronized(reader) {
+ termDocs = (mayUseTermDocs && reader.hasDeletions()) ? reader.termDocs(null) : null;
}
-
- if (includeLower && lowerPoint == 0) {
- throw new IllegalArgumentException("The lower bound must be non-null to be inclusive");
- } else if (includeLower && lowerPoint > 0) {
- inclusiveLowerPoint = lowerPoint;
- } else if (lowerPoint >= 0) {
- inclusiveLowerPoint = lowerPoint+1;
+ if (termDocs != null) {
+ // a DocIdSetIterator using TermDocs to iterate valid docIds
+ return new DocIdSetIterator() {
+ private int doc = -1;
+
+ /** @deprecated use {@link #nextDoc()} instead. */
+ public boolean next() throws IOException {
+ return nextDoc() != NO_MORE_DOCS;
+ }
+
+ /** @deprecated use {@link #advance(int)} instead. */
+ public boolean skipTo(int target) throws IOException {
+ return advance(target) != NO_MORE_DOCS;
+ }
+
+ /** @deprecated use {@link #docID()} instead. */
+ public int doc() {
+ return termDocs.doc();
+ }
+
+ public int docID() {
+ return doc;
+ }
+
+ public int nextDoc() throws IOException {
+ do {
+ if (!termDocs.next())
+ return doc = NO_MORE_DOCS;
+ } while (!matchDoc(doc = termDocs.doc()));
+ return doc;
+ }
+
+ public int advance(int target) throws IOException {
+ if (!termDocs.skipTo(target))
+ return doc = NO_MORE_DOCS;
+ while (!matchDoc(doc = termDocs.doc())) {
+ if (!termDocs.next())
+ return doc = NO_MORE_DOCS;
+ }
+ return doc;
+ }
+ };
} else {
- inclusiveLowerPoint = -lowerPoint-1;
- }
-
- if (includeUpper && upperPoint == 0) {
- throw new IllegalArgumentException("The upper bound must be non-null to be inclusive");
- } else if (upperPoint == 0) {
- inclusiveUpperPoint = Integer.MAX_VALUE;
- } else if (includeUpper && upperPoint > 0) {
- inclusiveUpperPoint = upperPoint;
- } else if (upperPoint >= 0) {
- inclusiveUpperPoint = upperPoint - 1;
- } else {
- inclusiveUpperPoint = -upperPoint - 2;
- }
- }
-
- public DocIdSetIterator iterator() {
- return new RangeMultiFilterIterator();
- }
-
- protected class RangeMultiFilterIterator extends DocIdSetIterator {
- private int doc = -1;
+ // a DocIdSetIterator generating docIds by incrementing a variable -
+ // this one can be used if there are no deletions are on the index
+ return new DocIdSetIterator() {
+ private int doc = -1;
+
+ /** @deprecated use {@link #nextDoc()} instead. */
+ public boolean next() throws IOException {
+ return nextDoc() != NO_MORE_DOCS;
+ }
+
+ /** @deprecated use {@link #advance(int)} instead. */
+ public boolean skipTo(int target) throws IOException {
+ return advance(target) != NO_MORE_DOCS;
+ }
- /** @deprecated use {@link #docID()} instead. */
- public int doc() {
- return doc;
- }
+ /** @deprecated use {@link #docID()} instead. */
+ public int doc() {
+ return doc;
+ }
- public int docID() {
- return doc;
- }
-
- /** @deprecated use {@link #nextDoc()} instead. */
- public boolean next() {
- return nextDoc() != NO_MORE_DOCS;
- }
-
- public int nextDoc() {
- try {
- do {
- doc++;
- } while (fcsi.order[doc] > inclusiveUpperPoint
- || fcsi.order[doc] < inclusiveLowerPoint);
- } catch (ArrayIndexOutOfBoundsException e) {
- doc = NO_MORE_DOCS;
- }
- return doc;
- }
-
- /** @deprecated use {@link #advance(int)} instead. */
- public boolean skipTo(int target) {
- return advance(target) != NO_MORE_DOCS;
- }
-
- public int advance(int target) {
- try {
- doc = target;
- while (fcsi.order[doc] > inclusiveUpperPoint
- || fcsi.order[doc] < inclusiveLowerPoint) {
- doc++;
+ public int docID() {
+ return doc;
}
- } catch (ArrayIndexOutOfBoundsException e) {
- doc = NO_MORE_DOCS;
- }
- return doc;
+
+ public int nextDoc() {
+ try {
+ do {
+ doc++;
+ } while (!matchDoc(doc));
+ return doc;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return doc = NO_MORE_DOCS;
+ }
+ }
+
+ public int advance(int target) {
+ try {
+ doc = target;
+ while (!matchDoc(doc)) {
+ doc++;
+ }
+ return doc;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return doc = NO_MORE_DOCS;
+ }
+ }
+ };
}
-
}
}
+
}
Index: src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java
===================================================================
--- src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java (revision 789589)
+++ src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java (working copy)
@@ -67,64 +67,64 @@
// test id, bounded on both ends
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,maxIP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,maxIP,T,T), numDocs).scoreDocs;
assertEquals("find all", numDocs, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,maxIP,T,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,maxIP,T,F), numDocs).scoreDocs;
assertEquals("all but last", numDocs-1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,maxIP,F,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,maxIP,F,T), numDocs).scoreDocs;
assertEquals("all but first", numDocs-1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,maxIP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,maxIP,F,F), numDocs).scoreDocs;
assertEquals("all but ends", numDocs-2, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",medIP,maxIP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",medIP,maxIP,T,T), numDocs).scoreDocs;
assertEquals("med and up", 1+ maxId-medId, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,medIP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,medIP,T,T), numDocs).scoreDocs;
assertEquals("up to med", 1+ medId-minId, result.length);
// unbounded id
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,null,T,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,null,T,F), numDocs).scoreDocs;
assertEquals("min and up", numDocs, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",null,maxIP,F,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",null,maxIP,F,T), numDocs).scoreDocs;
assertEquals("max and down", numDocs, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,null,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,null,F,F), numDocs).scoreDocs;
assertEquals("not min, but up", numDocs-1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",null,maxIP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",null,maxIP,F,F), numDocs).scoreDocs;
assertEquals("not max, but down", numDocs-1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",medIP,maxIP,T,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",medIP,maxIP,T,F), numDocs).scoreDocs;
assertEquals("med and up, not max", maxId-medId, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,medIP,F,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,medIP,F,T), numDocs).scoreDocs;
assertEquals("not min, up to med", medId-minId, result.length);
// very small sets
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,minIP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,minIP,F,F), numDocs).scoreDocs;
assertEquals("min,min,F,F", 0, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",medIP,medIP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",medIP,medIP,F,F), numDocs).scoreDocs;
assertEquals("med,med,F,F", 0, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",maxIP,maxIP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",maxIP,maxIP,F,F), numDocs).scoreDocs;
assertEquals("max,max,F,F", 0, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",minIP,minIP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",minIP,minIP,T,T), numDocs).scoreDocs;
assertEquals("min,min,T,T", 1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",null,minIP,F,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",null,minIP,F,T), numDocs).scoreDocs;
assertEquals("nul,min,F,T", 1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",maxIP,maxIP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",maxIP,maxIP,T,T), numDocs).scoreDocs;
assertEquals("max,max,T,T", 1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",maxIP,null,T,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",maxIP,null,T,F), numDocs).scoreDocs;
assertEquals("max,nul,T,T", 1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("id",medIP,medIP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("id",medIP,medIP,T,T), numDocs).scoreDocs;
assertEquals("med,med,T,T", 1, result.length);
}
@@ -146,47 +146,365 @@
// test extremes, bounded on both ends
- result = search.search(q,new FieldCacheRangeFilter("rand",minRP,maxRP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",minRP,maxRP,T,T), numDocs).scoreDocs;
assertEquals("find all", numDocs, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",minRP,maxRP,T,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",minRP,maxRP,T,F), numDocs).scoreDocs;
assertEquals("all but biggest", numDocs-1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",minRP,maxRP,F,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",minRP,maxRP,F,T), numDocs).scoreDocs;
assertEquals("all but smallest", numDocs-1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",minRP,maxRP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",minRP,maxRP,F,F), numDocs).scoreDocs;
assertEquals("all but extremes", numDocs-2, result.length);
// unbounded
- result = search.search(q,new FieldCacheRangeFilter("rand",minRP,null,T,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",minRP,null,T,F), numDocs).scoreDocs;
assertEquals("smallest and up", numDocs, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",null,maxRP,F,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",null,maxRP,F,T), numDocs).scoreDocs;
assertEquals("biggest and down", numDocs, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",minRP,null,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",minRP,null,F,F), numDocs).scoreDocs;
assertEquals("not smallest, but up", numDocs-1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",null,maxRP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",null,maxRP,F,F), numDocs).scoreDocs;
assertEquals("not biggest, but down", numDocs-1, result.length);
// very small sets
- result = search.search(q,new FieldCacheRangeFilter("rand",minRP,minRP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",minRP,minRP,F,F), numDocs).scoreDocs;
assertEquals("min,min,F,F", 0, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",maxRP,maxRP,F,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",maxRP,maxRP,F,F), numDocs).scoreDocs;
assertEquals("max,max,F,F", 0, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",minRP,minRP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",minRP,minRP,T,T), numDocs).scoreDocs;
assertEquals("min,min,T,T", 1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",null,minRP,F,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",null,minRP,F,T), numDocs).scoreDocs;
assertEquals("nul,min,F,T", 1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",maxRP,maxRP,T,T), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",maxRP,maxRP,T,T), numDocs).scoreDocs;
assertEquals("max,max,T,T", 1, result.length);
- result = search.search(q,new FieldCacheRangeFilter("rand",maxRP,null,T,F), numDocs).scoreDocs;
+ result = search.search(q,FieldCacheRangeFilter.newStringRange("rand",maxRP,null,T,F), numDocs).scoreDocs;
assertEquals("max,nul,T,T", 1, result.length);
}
+
+ // byte-ranges cannot be tested, because all ranges are too big for bytes, need an extra range for that
+
+ public void testFieldCacheRangeFilterShorts() throws IOException {
+
+ IndexReader reader = IndexReader.open(signedIndex.index);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ int numDocs = reader.numDocs();
+ int medId = ((maxId - minId) / 2);
+ Short minIdO = new Short((short) minId);
+ Short maxIdO = new Short((short) maxId);
+ Short medIdO = new Short((short) medId);
+
+ assertEquals("num of docs", numDocs, 1+ maxId - minId);
+
+ ScoreDoc[] result;
+ Query q = new TermQuery(new Term("body","body"));
+
+ // test id, bounded on both ends
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("find all", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,maxIdO,T,F), numDocs).scoreDocs;
+ assertEquals("all but last", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,maxIdO,F,T), numDocs).scoreDocs;
+ assertEquals("all but first", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("all but ends", numDocs-2, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",medIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("med and up", 1+ maxId-medId, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,medIdO,T,T), numDocs).scoreDocs;
+ assertEquals("up to med", 1+ medId-minId, result.length);
+
+ // unbounded id
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,null,T,F), numDocs).scoreDocs;
+ assertEquals("min and up", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",null,maxIdO,F,T), numDocs).scoreDocs;
+ assertEquals("max and down", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,null,F,F), numDocs).scoreDocs;
+ assertEquals("not min, but up", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",null,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("not max, but down", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",medIdO,maxIdO,T,F), numDocs).scoreDocs;
+ assertEquals("med and up, not max", maxId-medId, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,medIdO,F,T), numDocs).scoreDocs;
+ assertEquals("not min, up to med", medId-minId, result.length);
+
+ // very small sets
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,minIdO,F,F), numDocs).scoreDocs;
+ assertEquals("min,min,F,F", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",medIdO,medIdO,F,F), numDocs).scoreDocs;
+ assertEquals("med,med,F,F", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",maxIdO,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("max,max,F,F", 0, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",minIdO,minIdO,T,T), numDocs).scoreDocs;
+ assertEquals("min,min,T,T", 1, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",null,minIdO,F,T), numDocs).scoreDocs;
+ assertEquals("nul,min,F,T", 1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",maxIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("max,max,T,T", 1, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",maxIdO,null,T,F), numDocs).scoreDocs;
+ assertEquals("max,nul,T,T", 1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",medIdO,medIdO,T,T), numDocs).scoreDocs;
+ assertEquals("med,med,T,T", 1, result.length);
+
+ // special cases
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",new Short(Short.MAX_VALUE),null,F,F), numDocs).scoreDocs;
+ assertEquals("overflow special case", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",null,new Short(Short.MIN_VALUE),F,F), numDocs).scoreDocs;
+ assertEquals("overflow special case", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newShortRange("id",maxIdO,minIdO,T,T), numDocs).scoreDocs;
+ assertEquals("inverse range", 0, result.length);
+ }
+
+ public void testFieldCacheRangeFilterInts() throws IOException {
+
+ IndexReader reader = IndexReader.open(signedIndex.index);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ int numDocs = reader.numDocs();
+ int medId = ((maxId - minId) / 2);
+ Integer minIdO = new Integer(minId);
+ Integer maxIdO = new Integer(maxId);
+ Integer medIdO = new Integer(medId);
+
+ assertEquals("num of docs", numDocs, 1+ maxId - minId);
+
+ ScoreDoc[] result;
+ Query q = new TermQuery(new Term("body","body"));
+
+ // test id, bounded on both ends
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("find all", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,maxIdO,T,F), numDocs).scoreDocs;
+ assertEquals("all but last", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,maxIdO,F,T), numDocs).scoreDocs;
+ assertEquals("all but first", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("all but ends", numDocs-2, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",medIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("med and up", 1+ maxId-medId, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,medIdO,T,T), numDocs).scoreDocs;
+ assertEquals("up to med", 1+ medId-minId, result.length);
+
+ // unbounded id
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,null,T,F), numDocs).scoreDocs;
+ assertEquals("min and up", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",null,maxIdO,F,T), numDocs).scoreDocs;
+ assertEquals("max and down", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,null,F,F), numDocs).scoreDocs;
+ assertEquals("not min, but up", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",null,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("not max, but down", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",medIdO,maxIdO,T,F), numDocs).scoreDocs;
+ assertEquals("med and up, not max", maxId-medId, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,medIdO,F,T), numDocs).scoreDocs;
+ assertEquals("not min, up to med", medId-minId, result.length);
+
+ // very small sets
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,minIdO,F,F), numDocs).scoreDocs;
+ assertEquals("min,min,F,F", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",medIdO,medIdO,F,F), numDocs).scoreDocs;
+ assertEquals("med,med,F,F", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",maxIdO,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("max,max,F,F", 0, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",minIdO,minIdO,T,T), numDocs).scoreDocs;
+ assertEquals("min,min,T,T", 1, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",null,minIdO,F,T), numDocs).scoreDocs;
+ assertEquals("nul,min,F,T", 1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",maxIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("max,max,T,T", 1, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",maxIdO,null,T,F), numDocs).scoreDocs;
+ assertEquals("max,nul,T,T", 1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",medIdO,medIdO,T,T), numDocs).scoreDocs;
+ assertEquals("med,med,T,T", 1, result.length);
+
+ // special cases
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",new Integer(Integer.MAX_VALUE),null,F,F), numDocs).scoreDocs;
+ assertEquals("overflow special case", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",null,new Integer(Integer.MIN_VALUE),F,F), numDocs).scoreDocs;
+ assertEquals("overflow special case", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newIntRange("id",maxIdO,minIdO,T,T), numDocs).scoreDocs;
+ assertEquals("inverse range", 0, result.length);
+ }
+
+ public void testFieldCacheRangeFilterLongs() throws IOException {
+
+ IndexReader reader = IndexReader.open(signedIndex.index);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ int numDocs = reader.numDocs();
+ int medId = ((maxId - minId) / 2);
+ Long minIdO = new Long(minId);
+ Long maxIdO = new Long(maxId);
+ Long medIdO = new Long(medId);
+
+ assertEquals("num of docs", numDocs, 1+ maxId - minId);
+
+ ScoreDoc[] result;
+ Query q = new TermQuery(new Term("body","body"));
+
+ // test id, bounded on both ends
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("find all", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,maxIdO,T,F), numDocs).scoreDocs;
+ assertEquals("all but last", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,maxIdO,F,T), numDocs).scoreDocs;
+ assertEquals("all but first", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("all but ends", numDocs-2, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",medIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("med and up", 1+ maxId-medId, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,medIdO,T,T), numDocs).scoreDocs;
+ assertEquals("up to med", 1+ medId-minId, result.length);
+
+ // unbounded id
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,null,T,F), numDocs).scoreDocs;
+ assertEquals("min and up", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",null,maxIdO,F,T), numDocs).scoreDocs;
+ assertEquals("max and down", numDocs, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,null,F,F), numDocs).scoreDocs;
+ assertEquals("not min, but up", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",null,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("not max, but down", numDocs-1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",medIdO,maxIdO,T,F), numDocs).scoreDocs;
+ assertEquals("med and up, not max", maxId-medId, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,medIdO,F,T), numDocs).scoreDocs;
+ assertEquals("not min, up to med", medId-minId, result.length);
+
+ // very small sets
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,minIdO,F,F), numDocs).scoreDocs;
+ assertEquals("min,min,F,F", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",medIdO,medIdO,F,F), numDocs).scoreDocs;
+ assertEquals("med,med,F,F", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",maxIdO,maxIdO,F,F), numDocs).scoreDocs;
+ assertEquals("max,max,F,F", 0, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",minIdO,minIdO,T,T), numDocs).scoreDocs;
+ assertEquals("min,min,T,T", 1, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",null,minIdO,F,T), numDocs).scoreDocs;
+ assertEquals("nul,min,F,T", 1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",maxIdO,maxIdO,T,T), numDocs).scoreDocs;
+ assertEquals("max,max,T,T", 1, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",maxIdO,null,T,F), numDocs).scoreDocs;
+ assertEquals("max,nul,T,T", 1, result.length);
+
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",medIdO,medIdO,T,T), numDocs).scoreDocs;
+ assertEquals("med,med,T,T", 1, result.length);
+
+ // special cases
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",new Long(Long.MAX_VALUE),null,F,F), numDocs).scoreDocs;
+ assertEquals("overflow special case", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",null,new Long(Long.MIN_VALUE),F,F), numDocs).scoreDocs;
+ assertEquals("overflow special case", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newLongRange("id",maxIdO,minIdO,T,T), numDocs).scoreDocs;
+ assertEquals("inverse range", 0, result.length);
+ }
+
+ // float and double tests are a bit minimalistic, but its complicated, because missing precision
+
+ public void testFieldCacheRangeFilterFloats() throws IOException {
+
+ IndexReader reader = IndexReader.open(signedIndex.index);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ int numDocs = reader.numDocs();
+ Float minIdO = new Float(minId + .5f);
+ Float medIdO = new Float(minIdO.floatValue() + ((float) (maxId-minId))/2.0f);
+
+ ScoreDoc[] result;
+ Query q = new TermQuery(new Term("body","body"));
+
+ result = search.search(q,FieldCacheRangeFilter.newFloatRange("id",minIdO,medIdO,T,T), numDocs).scoreDocs;
+ assertEquals("find all", numDocs/2, result.length);
+ int count = 0;
+ result = search.search(q,FieldCacheRangeFilter.newFloatRange("id",null,medIdO,F,T), numDocs).scoreDocs;
+ count += result.length;
+ result = search.search(q,FieldCacheRangeFilter.newFloatRange("id",medIdO,null,F,F), numDocs).scoreDocs;
+ count += result.length;
+ assertEquals("sum of two concenatted ranges", numDocs, count);
+ result = search.search(q,FieldCacheRangeFilter.newFloatRange("id",new Float(Float.POSITIVE_INFINITY),null,F,F), numDocs).scoreDocs;
+ assertEquals("infinity special case", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newFloatRange("id",null,new Float(Float.NEGATIVE_INFINITY),F,F), numDocs).scoreDocs;
+ assertEquals("infinity special case", 0, result.length);
+ }
+
+ public void testFieldCacheRangeFilterDoubles() throws IOException {
+
+ IndexReader reader = IndexReader.open(signedIndex.index);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ int numDocs = reader.numDocs();
+ Double minIdO = new Double(minId + .5);
+ Double medIdO = new Double(minIdO.floatValue() + ((double) (maxId-minId))/2.0);
+
+ ScoreDoc[] result;
+ Query q = new TermQuery(new Term("body","body"));
+
+ result = search.search(q,FieldCacheRangeFilter.newDoubleRange("id",minIdO,medIdO,T,T), numDocs).scoreDocs;
+ assertEquals("find all", numDocs/2, result.length);
+ int count = 0;
+ result = search.search(q,FieldCacheRangeFilter.newDoubleRange("id",null,medIdO,F,T), numDocs).scoreDocs;
+ count += result.length;
+ result = search.search(q,FieldCacheRangeFilter.newDoubleRange("id",medIdO,null,F,F), numDocs).scoreDocs;
+ count += result.length;
+ assertEquals("sum of two concenatted ranges", numDocs, count);
+ result = search.search(q,FieldCacheRangeFilter.newDoubleRange("id",new Double(Double.POSITIVE_INFINITY),null,F,F), numDocs).scoreDocs;
+ assertEquals("infinity special case", 0, result.length);
+ result = search.search(q,FieldCacheRangeFilter.newDoubleRange("id",null, new Double(Double.NEGATIVE_INFINITY),F,F), numDocs).scoreDocs;
+ assertEquals("infinity special case", 0, result.length);
+ }
+
}