mirror of https://github.com/apache/lucene.git
LUCENE-1461: Add support for numeric types to FieldCacheRangeFilter
git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@789682 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8304b88578
commit
f3896a58df
|
@ -385,9 +385,12 @@ Bug fixes
|
|||
your documents all have a single term for a given field, and you
|
||||
need to create many RangeFilters with varying lower/upper bounds,
|
||||
then this is likely a much faster way to create the filters than
|
||||
RangeFilter. However, it comes at the expense of added RAM
|
||||
consumption and slower first-time usage due to populating the
|
||||
FieldCache. (Tim Sturge, Matt Ericson via Mike McCandless)
|
||||
RangeFilter. FieldCacheRangeFilter allows ranges on all data types,
|
||||
FieldCache supports (term ranges, byte, short, int, long, float, double).
|
||||
However, it comes at the expense of added RAM consumption and slower
|
||||
first-time usage due to populating the FieldCache. It also does not
|
||||
support collation (Tim Sturge, Matt Ericson via Mike McCandless and
|
||||
Uwe Schindler)
|
||||
|
||||
8. LUCENE-1296: add protected method CachingWrapperFilter.docIdSetToCache
|
||||
to allow subclasses to choose which DocIdSet implementation to use
|
||||
|
|
|
@ -19,74 +19,430 @@ package org.apache.lucene.search;
|
|||
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).
|
||||
*
|
||||
* FieldCacheRangeFilter builds a single cache for the field the first time it is used.
|
||||
* A range filter built on top of a cached single term field (in {@link FieldCache}).
|
||||
*
|
||||
* <p>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.
|
||||
* <p>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).
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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 <code>NOT_ANALYZED</code> field to ensure that
|
||||
* there is only a single term.
|
||||
*
|
||||
* <p>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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 @@ public class FieldCacheRangeFilter extends Filter {
|
|||
) { 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;
|
||||
} 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;
|
||||
|
||||
/** @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++;
|
||||
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;
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
doc = NO_MORE_DOCS;
|
||||
}
|
||||
return doc;
|
||||
|
||||
/** @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 {
|
||||
// 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;
|
||||
}
|
||||
|
||||
public int docID() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,64 +67,64 @@ public class TestFieldCacheRangeFilter extends BaseTestRangeFilter {
|
|||
|
||||
// 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 @@ public class TestFieldCacheRangeFilter extends BaseTestRangeFilter {
|
|||
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue