lucene-java-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From uschind...@apache.org
Subject svn commit: r789682 - in /lucene/java/trunk: CHANGES.txt src/java/org/apache/lucene/search/FieldCacheRangeFilter.java src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java
Date Tue, 30 Jun 2009 11:17:15 GMT
Author: uschindler
Date: Tue Jun 30 11:17:14 2009
New Revision: 789682

URL: http://svn.apache.org/viewvc?rev=789682&view=rev
Log:
LUCENE-1461: Add support for numeric types to FieldCacheRangeFilter

Modified:
    lucene/java/trunk/CHANGES.txt
    lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java
    lucene/java/trunk/src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java

Modified: lucene/java/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/java/trunk/CHANGES.txt?rev=789682&r1=789681&r2=789682&view=diff
==============================================================================
--- lucene/java/trunk/CHANGES.txt (original)
+++ lucene/java/trunk/CHANGES.txt Tue Jun 30 11:17:14 2009
@@ -385,9 +385,12 @@
     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

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java?rev=789682&r1=789681&r2=789682&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheRangeFilter.java Tue Jun 30 11:17:14 2009
@@ -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).
- * 
- * 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.
+ *
+ * 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).
  *
- * 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. 
+ * <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 FieldCacheRangeFilter(
-        String field, 
-        String lowerVal,
-        String upperVal,
-        boolean includeLower,
-        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;
+  
+  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 @@
     ) { 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);
-
-      if (lowerPoint == 0 && upperPoint == 0) {
-        throw new IllegalArgumentException("At least one value must be non-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;
+  
+    /** this method checks, if a doc is a hit, should throw AIOBE, when position invalid */
+    abstract boolean matchDoc(int doc) throws ArrayIndexOutOfBoundsException;
+
+    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 (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;
+      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 {
-        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++;
+        // 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;
           }
-        } 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 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;
+            }
+          }
+        };
       }
-      
     }
   }
+
 }

Modified: lucene/java/trunk/src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java?rev=789682&r1=789681&r2=789682&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java Tue Jun 30 11:17:14 2009
@@ -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);
+  }
+  
 }



Mime
View raw message