You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by tf...@apache.org on 2017/07/26 17:17:22 UTC

lucene-solr:branch_7_0: SOLR-11070, SOLR-11056: Make docValues range queries behave the same as Trie/Point fields for Double/Float Infinity cases

Repository: lucene-solr
Updated Branches:
  refs/heads/branch_7_0 a95168caf -> 9372270f3


SOLR-11070, SOLR-11056: Make docValues range queries behave the same as Trie/Point fields for Double/Float Infinity cases


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/9372270f
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/9372270f
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/9372270f

Branch: refs/heads/branch_7_0
Commit: 9372270f3f0c69634d169bd9dcf959b1eb2b2218
Parents: a95168c
Author: Tomas Fernandez Lobbe <tf...@apache.org>
Authored: Wed Jul 26 10:10:40 2017 -0700
Committer: Tomas Fernandez Lobbe <tf...@apache.org>
Committed: Wed Jul 26 10:17:08 2017 -0700

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   6 +
 .../apache/solr/schema/DoublePointField.java    |   4 +-
 .../org/apache/solr/schema/FloatPointField.java |   4 +-
 .../apache/solr/schema/NumericFieldType.java    | 149 ++++++---
 .../solr/collection1/conf/schema11.xml          |   9 +
 .../org/apache/solr/schema/TestPointFields.java | 135 +++++---
 .../org/apache/solr/search/TestRangeQuery.java  | 305 ++++++++++++++++++-
 7 files changed, 527 insertions(+), 85 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9372270f/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 57e0486..c80103c 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -317,6 +317,9 @@ Optimizations
 * SOLR-10727: Avoid polluting the filter cache for certain types of faceting (typically ranges) when
   the base docset is empty. (David Smiley)
 
+* SOLR-11070: Make docValues range queries behave the same as Trie/Point fields for Double/Float Infinity cases
+  (Tomás Fernández Löbbe, Andrey Kudryavtsev)
+
 Other Changes
 ----------------------
 * SOLR-10236: Removed FieldType.getNumericType(). Use getNumberType() instead. (Tomás Fernández Löbbe)
@@ -461,6 +464,9 @@ Other Changes
 
 * SOLR-10760: Remove trie field types and fields from example schemas. (Steve Rowe)
 
+* SOLR-11056: Add random range query test that compares results across Trie*, *Point and DocValue-only fields
+  (Tomás Fernández Löbbe)
+
 ==================  6.7.0 ==================
 
 Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9372270f/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
index d2cf6ed..edc7c38 100644
--- a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
@@ -18,7 +18,6 @@
 package org.apache.solr.schema;
 
 import java.util.Collection;
-
 import org.apache.lucene.document.DoublePoint;
 import org.apache.lucene.document.StoredField;
 import org.apache.lucene.index.DocValuesType;
@@ -26,6 +25,7 @@ import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.DoubleFieldSource;
 import org.apache.lucene.queries.function.valuesource.MultiValuedDoubleFieldSource;
+import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.SortedNumericSelector;
@@ -63,6 +63,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
     } else {
       actualMin = parseDoubleFromUser(field.getName(), min);
       if (!minInclusive) {
+        if (actualMin == Double.POSITIVE_INFINITY) return new MatchNoDocsQuery();
         actualMin = DoublePoint.nextUp(actualMin);
       }
     }
@@ -71,6 +72,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
     } else {
       actualMax = parseDoubleFromUser(field.getName(), max);
       if (!maxInclusive) {
+        if (actualMax == Double.NEGATIVE_INFINITY) return new MatchNoDocsQuery();
         actualMax = DoublePoint.nextDown(actualMax);
       }
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9372270f/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
index e1a9741..c70655a 100644
--- a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
@@ -18,7 +18,6 @@
 package org.apache.solr.schema;
 
 import java.util.Collection;
-
 import org.apache.lucene.document.FloatPoint;
 import org.apache.lucene.document.StoredField;
 import org.apache.lucene.index.DocValuesType;
@@ -26,6 +25,7 @@ import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.FloatFieldSource;
 import org.apache.lucene.queries.function.valuesource.MultiValuedFloatFieldSource;
+import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.SortedNumericSelector;
@@ -63,6 +63,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
     } else {
       actualMin = parseFloatFromUser(field.getName(), min);
       if (!minInclusive) {
+        if (actualMin == Float.POSITIVE_INFINITY) return new MatchNoDocsQuery();
         actualMin = FloatPoint.nextUp(actualMin);
       }
     }
@@ -71,6 +72,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
     } else {
       actualMax = parseFloatFromUser(field.getName(), max);
       if (!maxInclusive) {
+        if (actualMax == Float.NEGATIVE_INFINITY) return new MatchNoDocsQuery();
         actualMax = FloatPoint.nextDown(actualMax);
       }
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9372270f/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java b/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
index c79f465..86697e5 100644
--- a/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
@@ -16,6 +16,8 @@
  */
 package org.apache.solr.schema;
 
+import org.apache.lucene.document.DoublePoint;
+import org.apache.lucene.document.FloatPoint;
 import org.apache.lucene.document.NumericDocValuesField;
 import org.apache.lucene.document.SortedNumericDocValuesField;
 import org.apache.lucene.queries.function.ValueSource;
@@ -40,14 +42,8 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
     return type;
   }
 
-  private static long FLOAT_NEGATIVE_INFINITY_BITS = (long)Float.floatToIntBits(Float.NEGATIVE_INFINITY);
-  private static long DOUBLE_NEGATIVE_INFINITY_BITS = Double.doubleToLongBits(Double.NEGATIVE_INFINITY);
-  private static long FLOAT_POSITIVE_INFINITY_BITS = (long)Float.floatToIntBits(Float.POSITIVE_INFINITY);
-  private static long DOUBLE_POSITIVE_INFINITY_BITS = Double.doubleToLongBits(Double.POSITIVE_INFINITY);
   private static long FLOAT_MINUS_ZERO_BITS = (long)Float.floatToIntBits(-0f);
   private static long DOUBLE_MINUS_ZERO_BITS = Double.doubleToLongBits(-0d);
-  private static long FLOAT_ZERO_BITS = (long)Float.floatToIntBits(0f);
-  private static long DOUBLE_ZERO_BITS = Double.doubleToLongBits(0d);
 
   protected Query getDocValuesRangeQuery(QParser parser, SchemaField field, String min, String max,
       boolean minInclusive, boolean maxInclusive) {
@@ -89,50 +85,119 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
   protected Query getRangeQueryForFloatDoubleDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
     Query query;
     String fieldName = sf.getName();
-
-    Number minVal = min == null ? null : getNumberType() == NumberType.FLOAT ? parseFloatFromUser(sf.getName(), min): parseDoubleFromUser(sf.getName(), min);
-    Number maxVal = max == null ? null : getNumberType() == NumberType.FLOAT ? parseFloatFromUser(sf.getName(), max): parseDoubleFromUser(sf.getName(), max);
-    
-    Long minBits = 
-        min == null ? null : getNumberType() == NumberType.FLOAT ? (long) Float.floatToIntBits(minVal.floatValue()): Double.doubleToLongBits(minVal.doubleValue());
-    Long maxBits = 
-        max == null ? null : getNumberType() == NumberType.FLOAT ? (long) Float.floatToIntBits(maxVal.floatValue()): Double.doubleToLongBits(maxVal.doubleValue());
-    
-    long negativeInfinityBits = getNumberType() == NumberType.FLOAT ? FLOAT_NEGATIVE_INFINITY_BITS : DOUBLE_NEGATIVE_INFINITY_BITS;
-    long positiveInfinityBits = getNumberType() == NumberType.FLOAT ? FLOAT_POSITIVE_INFINITY_BITS : DOUBLE_POSITIVE_INFINITY_BITS;
-    long minusZeroBits = getNumberType() == NumberType.FLOAT ? FLOAT_MINUS_ZERO_BITS : DOUBLE_MINUS_ZERO_BITS;
-    long zeroBits = getNumberType() == NumberType.FLOAT ? FLOAT_ZERO_BITS : DOUBLE_ZERO_BITS;
-    
+    long minBits, maxBits;
+    boolean minNegative, maxNegative;
+    Number minVal, maxVal;
+    if (getNumberType() == NumberType.FLOAT) {
+      if (min == null) {
+        minVal = Float.NEGATIVE_INFINITY;
+      } else {
+        minVal = parseFloatFromUser(sf.getName(), min);
+        if (!minInclusive) {
+          if (minVal.floatValue() == Float.POSITIVE_INFINITY) return new MatchNoDocsQuery();
+          minVal = FloatPoint.nextUp(minVal.floatValue());
+        }
+      }
+      if (max == null) {
+        maxVal = Float.POSITIVE_INFINITY;
+      } else {
+        maxVal = parseFloatFromUser(sf.getName(), max);
+        if (!maxInclusive) {
+          if (maxVal.floatValue() == Float.NEGATIVE_INFINITY) return new MatchNoDocsQuery();
+          maxVal = FloatPoint.nextDown(maxVal.floatValue());
+        }
+      }
+      minBits = Float.floatToIntBits(minVal.floatValue());
+      maxBits = Float.floatToIntBits(maxVal.floatValue());
+      minNegative = minVal.floatValue() < 0f || minBits == FLOAT_MINUS_ZERO_BITS;
+      maxNegative = maxVal.floatValue() < 0f || maxBits == FLOAT_MINUS_ZERO_BITS;
+    } else {
+      assert getNumberType() == NumberType.DOUBLE;
+      if (min == null) {
+        minVal = Double.NEGATIVE_INFINITY;
+      } else {
+        minVal = parseDoubleFromUser(sf.getName(), min);
+        if (!minInclusive) {
+          if (minVal.doubleValue() == Double.POSITIVE_INFINITY) return new MatchNoDocsQuery();
+          minVal = DoublePoint.nextUp(minVal.doubleValue());
+        }
+      }
+      if (max == null) {
+        maxVal = Double.POSITIVE_INFINITY;
+      } else {
+        maxVal = parseDoubleFromUser(sf.getName(), max);
+        if (!maxInclusive) {
+          if (maxVal.doubleValue() == Double.NEGATIVE_INFINITY) return new MatchNoDocsQuery();
+          maxVal = DoublePoint.nextDown(maxVal.doubleValue());
+        }
+      }
+      minBits = Double.doubleToLongBits(minVal.doubleValue());
+      maxBits = Double.doubleToLongBits(maxVal.doubleValue());
+      minNegative = minVal.doubleValue() < 0d || minBits == DOUBLE_MINUS_ZERO_BITS;
+      maxNegative = maxVal.doubleValue() < 0d || maxBits == DOUBLE_MINUS_ZERO_BITS;
+    }
     // If min is negative (or -0d) and max is positive (or +0d), then issue a FunctionRangeQuery
-    if ((minVal == null || minVal.doubleValue() < 0d || minBits == minusZeroBits) && 
-        (maxVal == null || (maxVal.doubleValue() > 0d || maxBits == zeroBits))) {
-
+    if (minNegative && !maxNegative) {
       ValueSource vs = getValueSource(sf, null);
-      query = new FunctionRangeQuery(new ValueSourceRangeFilter(vs, min, max, minInclusive, maxInclusive));
-
-    } else { // If both max and min are negative (or -0d), then issue range query with max and min reversed
-      if ((minVal == null || minVal.doubleValue() < 0d || minBits == minusZeroBits) &&
-          (maxVal != null && (maxVal.doubleValue() < 0d || maxBits == minusZeroBits))) {
-        query = numericDocValuesRangeQuery
-            (fieldName, maxBits, (min == null ? Long.valueOf(negativeInfinityBits) : minBits), maxInclusive, minInclusive, false);
-      } else { // If both max and min are positive, then issue range query
-        query = numericDocValuesRangeQuery
-            (fieldName, minBits, (max == null ? Long.valueOf(positiveInfinityBits) : maxBits), minInclusive, maxInclusive, false);
-      }
+      query = new FunctionRangeQuery(new ValueSourceRangeFilter(vs, minVal.toString(), maxVal.toString(), true, true));
+    } else if (minNegative && maxNegative) {// If both max and min are negative (or -0d), then issue range query with max and min reversed
+      query = numericDocValuesRangeQuery
+          (fieldName, maxBits, minBits, true, true, false);
+    } else { // If both max and min are positive, then issue range query
+      query = numericDocValuesRangeQuery
+          (fieldName, minBits, maxBits, true, true, false);
     }
     return query;
   }
-  
+
   protected Query getRangeQueryForMultiValuedDoubleDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
-    Long minBits = min == null ? NumericUtils.doubleToSortableLong(Double.NEGATIVE_INFINITY): NumericUtils.doubleToSortableLong(parseDoubleFromUser(sf.getName(), min));
-    Long maxBits = max == null ? NumericUtils.doubleToSortableLong(Double.POSITIVE_INFINITY): NumericUtils.doubleToSortableLong(parseDoubleFromUser(sf.getName(), max));
-    return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, minInclusive, maxInclusive, true);
+    double minVal,maxVal;
+    if (min == null) {
+      minVal = Double.NEGATIVE_INFINITY;
+    } else {
+      minVal = parseDoubleFromUser(sf.getName(), min);
+      if (!minInclusive) {
+        if (minVal == Double.POSITIVE_INFINITY) return new MatchNoDocsQuery();
+        minVal = DoublePoint.nextUp(minVal);
+      }
+    }
+    if (max == null) {
+      maxVal = Double.POSITIVE_INFINITY;
+    } else {
+      maxVal = parseDoubleFromUser(sf.getName(), max);
+      if (!maxInclusive) {
+        if (maxVal == Double.NEGATIVE_INFINITY) return new MatchNoDocsQuery();
+        maxVal = DoublePoint.nextDown(maxVal);
+      }
+    }
+    Long minBits = NumericUtils.doubleToSortableLong(minVal);
+    Long maxBits = NumericUtils.doubleToSortableLong(maxVal);
+    return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, true, true, true);
   }
-  
+
   protected Query getRangeQueryForMultiValuedFloatDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
-    Long minBits = (long)(min == null ? NumericUtils.floatToSortableInt(Float.NEGATIVE_INFINITY): NumericUtils.floatToSortableInt(parseFloatFromUser(sf.getName(), min)));
-    Long maxBits = (long)(max == null ? NumericUtils.floatToSortableInt(Float.POSITIVE_INFINITY): NumericUtils.floatToSortableInt(parseFloatFromUser(sf.getName(), max)));
-    return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, minInclusive, maxInclusive, true);
+    float minVal,maxVal;
+    if (min == null) {
+      minVal = Float.NEGATIVE_INFINITY;
+    } else {
+      minVal = parseFloatFromUser(sf.getName(), min);
+      if (!minInclusive) {
+        if (minVal == Float.POSITIVE_INFINITY) return new MatchNoDocsQuery();
+        minVal = FloatPoint.nextUp(minVal);
+      }
+    }
+    if (max == null) {
+      maxVal = Float.POSITIVE_INFINITY;
+    } else {
+      maxVal = parseFloatFromUser(sf.getName(), max);
+      if (!maxInclusive) {
+        if (maxVal == Float.NEGATIVE_INFINITY) return new MatchNoDocsQuery();
+        maxVal = FloatPoint.nextDown(maxVal);
+      }
+    }
+    Long minBits = (long)NumericUtils.floatToSortableInt(minVal);
+    Long maxBits = (long)NumericUtils.floatToSortableInt(maxVal);
+    return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, true, true, true);
   }
   
   public static Query numericDocValuesRangeQuery(

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9372270f/solr/core/src/test-files/solr/collection1/conf/schema11.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema11.xml b/solr/core/src/test-files/solr/collection1/conf/schema11.xml
index 647e1ef..674c25f 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema11.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema11.xml
@@ -412,7 +412,16 @@ valued. -->
    <dynamicField name="*_dt_ni_p"   type="pdate"    indexed="false"  stored="true" docValues="true"/>
    <dynamicField name="*_dts_ni_p"   type="pdate"    indexed="false"  stored="true" docValues="true" multiValued="true"/>
 
+   <dynamicField name="*_i_ndv_p"    type="pint"    indexed="true"  stored="true" docValues="false" multiValued="false"/>
    <dynamicField name="*_is_ndv_p"   type="pint"    indexed="true"  stored="true" docValues="false" multiValued="true"/>
+   <dynamicField name="*_l_ndv_p"    type="plong"    indexed="true"  stored="true" docValues="false" multiValued="false"/>
+   <dynamicField name="*_ls_ndv_p"   type="plong"    indexed="true"  stored="true" docValues="false" multiValued="true"/>
+   <dynamicField name="*_f_ndv_p"    type="pfloat"    indexed="true"  stored="true" docValues="false" multiValued="false"/>
+   <dynamicField name="*_fs_ndv_p"   type="pfloat"    indexed="true"  stored="true" docValues="false" multiValued="true"/>
+   <dynamicField name="*_d_ndv_p"    type="pdouble"    indexed="true"  stored="true" docValues="false" multiValued="false"/>
+   <dynamicField name="*_ds_ndv_p"   type="pdouble"    indexed="true"  stored="true" docValues="false" multiValued="true"/>
+   <dynamicField name="*_dt_ndv_p"    type="pdate"    indexed="true"  stored="true" docValues="false" multiValued="false"/>
+   <dynamicField name="*_dts_ndv_p"   type="pdate"    indexed="true"  stored="true" docValues="false" multiValued="true"/>
 
    <dynamicField name="*_t"  type="text"    indexed="true"  stored="true"/>
    <dynamicField name="*_b"  type="boolean" indexed="true"  stored="true"/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9372270f/solr/core/src/test/org/apache/solr/schema/TestPointFields.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/schema/TestPointFields.java b/solr/core/src/test/org/apache/solr/schema/TestPointFields.java
index 04d704d..8192406 100644
--- a/solr/core/src/test/org/apache/solr/schema/TestPointFields.java
+++ b/solr/core/src/test/org/apache/solr/schema/TestPointFields.java
@@ -2648,8 +2648,9 @@ public class TestPointFields extends SolrTestCaseJ4 {
 
   private void doTestPointFieldMultiValuedRangeQuery(String fieldName, String type, String[] numbers) throws Exception {
     assert numbers != null && numbers.length == 20;
-    assertTrue(h.getCore().getLatestSchema().getField(fieldName).multiValued());
-    assertTrue(h.getCore().getLatestSchema().getField(fieldName).getType() instanceof PointField);
+    SchemaField sf = h.getCore().getLatestSchema().getField(fieldName);
+    assertTrue(sf.multiValued());
+    assertTrue(sf.getType() instanceof PointField);
     for (int i=9; i >= 0; i--) {
       assertU(adoc("id", String.valueOf(i), fieldName, numbers[i], fieldName, numbers[i+10]));
     }
@@ -2723,6 +2724,11 @@ public class TestPointFields extends SolrTestCaseJ4 {
                 "fl", "id, " + fieldName, "sort", "id asc"),
         "//*[@numFound='1']",
         "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']");
+    
+    if (sf.getType().getNumberType() == NumberType.FLOAT || sf.getType().getNumberType() == NumberType.DOUBLE) {
+      doTestDoubleFloatRangeLimits(fieldName, sf.getType().getNumberType() == NumberType.DOUBLE);
+    }
+    
   }
 
   private void doTestPointFieldMultiValuedFacetField(String nonDocValuesField, String dvFieldName, String[] numbers) throws Exception {
@@ -3191,45 +3197,98 @@ public class TestPointFields extends SolrTestCaseJ4 {
       assertQ(req("q", fieldName + ":{" + arr[0] + " TO " + arr[i] + "}", "fl", "id, " + fieldName), 
           "//*[@numFound='" + (Math.max(0,  i-1)) + "']");
     }
+    doTestDoubleFloatRangeLimits(fieldName, testDouble);
+  }
+  
+  private void doTestDoubleFloatRangeLimits(String fieldName, boolean testDouble) {
+    // POSITIVE/NEGATIVE_INFINITY toString is the same for Double and Float, it's OK to use this code for both cases
+    String positiveInfinity = String.valueOf(Double.POSITIVE_INFINITY);
+    String negativeInfinity = String.valueOf(Double.NEGATIVE_INFINITY);
+    String minVal = String.valueOf(testDouble?Double.MIN_VALUE:Float.MIN_VALUE);
+    String maxVal = String.valueOf(testDouble?Double.MAX_VALUE:Float.MAX_VALUE);
+    String negativeMinVal = "-" + minVal;
+    String negativeMaxVal =  "-" + maxVal;
+    clearIndex();
+    assertU(adoc("id", "1", fieldName, minVal));
+    assertU(adoc("id", "2", fieldName, maxVal));
+    assertU(adoc("id", "3", fieldName, negativeInfinity));
+    assertU(adoc("id", "4", fieldName, positiveInfinity));
+    assertU(adoc("id", "5", fieldName, negativeMinVal));
+    assertU(adoc("id", "6", fieldName, negativeMaxVal));
+    assertU(commit());
+    //negative to negative
+    assertAllInclusiveExclusiveVariations(fieldName, "*", "-1", 2, 2, 2, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, "-1", 1, 2, 1, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeMaxVal, negativeMinVal, 0, 1, 1, 2);
+    //negative to cero
+    assertAllInclusiveExclusiveVariations(fieldName, "*", "-0.0f", 3, 3, 3, 3);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, "-0.0f", 2, 3, 2, 3);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeMinVal, "-0.0f", 0, 1, 0, 1);
+    
+    assertAllInclusiveExclusiveVariations(fieldName, "*", "0", 3, 3, 3, 3);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, "0", 2, 3, 2, 3);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeMinVal, "0", 0, 1, 0, 1);
+    //negative to positive
+    assertAllInclusiveExclusiveVariations(fieldName, "*", "1", 4, 4, 4, 4);
+    assertAllInclusiveExclusiveVariations(fieldName, "-1", "*", 4, 4, 4, 4);
+    assertAllInclusiveExclusiveVariations(fieldName, "-1", "1", 2, 2, 2, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, "*", "*", 6, 6, 6, 6);
+    
+    assertAllInclusiveExclusiveVariations(fieldName, "-1", positiveInfinity, 3, 3, 4, 4);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, "1", 3, 4, 3, 4);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, positiveInfinity, 4, 5, 5, 6);
+    
+    assertAllInclusiveExclusiveVariations(fieldName, negativeMinVal, minVal, 0, 1, 1, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, negativeMaxVal, maxVal, 2, 3, 3, 4);
+    //cero to positive
+    assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", "*", 3, 3, 3, 3);
+    assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", positiveInfinity, 2, 2, 3, 3);
+    assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", minVal, 0, 0, 1, 1);
+    
+    assertAllInclusiveExclusiveVariations(fieldName, "0", "*", 3, 3, 3, 3);
+    assertAllInclusiveExclusiveVariations(fieldName, "0", positiveInfinity, 2, 2, 3, 3);
+    assertAllInclusiveExclusiveVariations(fieldName, "0", minVal, 0, 0, 1, 1);
+    //positive to positive
+    assertAllInclusiveExclusiveVariations(fieldName, "1", "*", 2, 2, 2, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, "1", positiveInfinity, 1, 1, 2, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, minVal, maxVal, 0, 1, 1, 2);
+    
+    // inverted limits
+    assertAllInclusiveExclusiveVariations(fieldName, "1", "-1", 0, 0, 0, 0);
+    assertAllInclusiveExclusiveVariations(fieldName, positiveInfinity, negativeInfinity, 0, 0, 0, 0);
+    assertAllInclusiveExclusiveVariations(fieldName, minVal, negativeMinVal, 0, 0, 0, 0);
+    
+    // MatchNoDocs cases
+    assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, negativeInfinity, 0, 0, 0, 1);
+    assertAllInclusiveExclusiveVariations(fieldName, positiveInfinity, positiveInfinity, 0, 0, 0, 1);
     
     clearIndex();
-    assertU(adoc("id", "1", fieldName, String.valueOf(Float.MAX_VALUE)));
-    assertU(adoc("id", "2", fieldName, String.valueOf(Float.MIN_VALUE)));
-    assertU(adoc("id", "3", fieldName, String.valueOf(Float.NEGATIVE_INFINITY)));
-    assertU(adoc("id", "4", fieldName, String.valueOf(Float.POSITIVE_INFINITY)));
-    assertU(commit());
-    assertQ(req("q", fieldName + ":[* TO *]", "fl", "id, " + fieldName), 
-        "//*[@numFound='4']");
-//    TODO: Awaits fix: SOLR-11070
-//    assertQ(req("q", fieldName + ":{* TO *}", "fl", "id, " + fieldName), 
-//        "//*[@numFound='4']");
-    assertQ(req("q", fieldName + ":[" + Float.MIN_VALUE + " TO " + Float.MAX_VALUE + "]", "fl", "id, " + fieldName), 
-        "//*[@numFound='2']");
-    assertQ(req("q", fieldName + ":{" + Float.MIN_VALUE + " TO " + Float.MAX_VALUE + "]", "fl", "id, " + fieldName), 
-        "//*[@numFound='1']");
-    assertQ(req("q", fieldName + ":[" + Float.MIN_VALUE + " TO " + Float.MAX_VALUE + "}", "fl", "id, " + fieldName), 
-        "//*[@numFound='1']");
-    if (testDouble) {
-      assertQ(req("q", fieldName + ":[" + Double.MIN_VALUE + " TO " + Double.MIN_VALUE + "}", "fl", "id, " + fieldName), 
-          "//*[@numFound='0']");
-      assertQ(req("q", fieldName + ":{" + Double.MAX_VALUE + " TO " + Double.MAX_VALUE + "]", "fl", "id, " + fieldName), 
-          "//*[@numFound='0']");
-      assertQ(req("q", fieldName + ":{" + Double.NEGATIVE_INFINITY + " TO " + Double.NEGATIVE_INFINITY + "]", "fl", "id, " + fieldName), 
-          "//*[@numFound='0']");
-      assertQ(req("q", fieldName + ":[" + Double.POSITIVE_INFINITY + " TO " + Double.POSITIVE_INFINITY + "}", "fl", "id, " + fieldName), 
-          "//*[@numFound='0']");
-    } else {
-      assertQ(req("q", fieldName + ":[" + Float.MIN_VALUE + " TO " + Float.MIN_VALUE + "}", "fl", "id, " + fieldName), 
-          "//*[@numFound='0']");
-      assertQ(req("q", fieldName + ":{" + Float.MAX_VALUE + " TO " + Float.MAX_VALUE + "]", "fl", "id, " + fieldName), 
-          "//*[@numFound='0']");
-      assertQ(req("q", fieldName + ":{" + Float.NEGATIVE_INFINITY + " TO " + Float.NEGATIVE_INFINITY + "]", "fl", "id, " + fieldName), 
-          "//*[@numFound='0']");
-      assertQ(req("q", fieldName + ":[" + Float.POSITIVE_INFINITY + " TO " + Float.POSITIVE_INFINITY + "}", "fl", "id, " + fieldName), 
-          "//*[@numFound='0']");
-    }
+    assertU(adoc("id", "1", fieldName, "0.0"));
+    assertU(adoc("id", "2", fieldName, "-0.0"));
+    assertU(commit());
+    assertAllInclusiveExclusiveVariations(fieldName, "*", "*", 2, 2, 2, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, "*", "0", 1, 1, 2, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, "0", "*", 0, 1, 0, 1);
+    assertAllInclusiveExclusiveVariations(fieldName, "*", "-0.0f", 0, 0, 1, 1);
+    assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", "*", 1, 2, 1, 2);
+    assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", "0", 0, 1, 1, 2);
+  }
+
+  private void assertAllInclusiveExclusiveVariations(String fieldName, String min, String max,
+      int countExclusiveExclusive,
+      int countInclusiveExclusive,
+      int countExclusiveInclusive,
+      int countInclusiveInclusive) {
+    assertQ(req("q", fieldName + ":{" + min + " TO " + max + "}", "fl", "id, " + fieldName), 
+        "//*[@numFound='" + countExclusiveExclusive +"']");
+    assertQ(req("q", fieldName + ":[" + min + " TO " + max + "}", "fl", "id, " + fieldName), 
+        "//*[@numFound='" + countInclusiveExclusive +"']");
+    assertQ(req("q", fieldName + ":{" + min + " TO " + max + "]", "fl", "id, " + fieldName), 
+        "//*[@numFound='" + countExclusiveInclusive +"']");
+    assertQ(req("q", fieldName + ":[" + min + " TO " + max + "]", "fl", "id, " + fieldName), 
+        "//*[@numFound='" + countInclusiveInclusive +"']");
   }
-  
+
   private void doTestFloatPointFunctionQuery(String field) throws Exception {
     assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
     int numVals = 10 * RANDOM_MULTIPLIER;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9372270f/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java b/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
index 6591218..763c8d5 100644
--- a/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
+++ b/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
@@ -16,20 +16,33 @@
  */
 package org.apache.solr.search;
 
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import org.apache.lucene.util.TestUtil;
-
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.ResultContext;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.NumberType;
+import org.apache.solr.schema.StrField;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import java.util.*;
-
 public class TestRangeQuery extends SolrTestCaseJ4 {
+  
+  private final static long DATE_START_TIME_RANDOM_TEST = 1499797224224L;
+  private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT);
 
   @BeforeClass
   public static void beforeClass() throws Exception {
@@ -385,6 +398,292 @@ public class TestRangeQuery extends SolrTestCaseJ4 {
     expectThrows(SyntaxError.class, () -> QParser.getParser("[A TO]", req("df", "text")).getQuery());
   }
 
+  public void testCompareTypesRandomRangeQueries() throws Exception {
+    int cardinality = 10000;
+    Map<NumberType,String[]> types = new HashMap<>(); //single and multivalued field types
+    Map<NumberType,String[]> typesMv = new HashMap<>(); // multivalued field types only
+    types.put(NumberType.INTEGER, new String[]{"ti", "ti_dv", "ti_ni_dv", "i_p", "i_ni_p", "i_ndv_p", "tis", "tis_dv", "tis_ni_dv", "is_p", "is_ni_p", "is_ndv_p"});
+    types.put(NumberType.LONG, new String[]{"tl", "tl_dv", "tl_ni_dv", "l_p", "l_ni_p", "l_ndv_p", "tls", "tls_dv", "tls_ni_dv", "ls_p", "ls_ni_p", "ls_ndv_p"});
+    types.put(NumberType.FLOAT, new String[]{"tf", "tf_dv", "tf_ni_dv", "f_p", "f_ni_p", "f_ndv_p", "tfs", "tfs_dv", "tfs_ni_dv", "fs_p", "fs_ni_p", "fs_ndv_p"});
+    types.put(NumberType.DOUBLE, new String[]{"td", "td_dv", "td_ni_dv", "d_p", "d_ni_p", "d_ndv_p", "tds", "tds_dv", "tds_ni_dv", "ds_p", "ds_ni_p", "ds_ndv_p"});
+    types.put(NumberType.DATE, new String[]{"tdt", "tdt_dv", "tdt_ni_dv", "dt_p", "dt_ni_p", "dt_ndv_p", "tdts", "tdts_dv", "tdts_ni_dv", "dts_p", "dts_ni_p", "dts_ndv_p"});
+    typesMv.put(NumberType.INTEGER, new String[]{"tis", "tis_dv", "tis_ni_dv", "is_p", "is_ni_p", "is_ndv_p"});
+    typesMv.put(NumberType.LONG, new String[]{"tls", "tls_dv", "tls_ni_dv", "ls_p", "ls_ni_p", "ls_ndv_p"});
+    typesMv.put(NumberType.FLOAT, new String[]{"tfs", "tfs_dv", "tfs_ni_dv", "fs_p", "fs_ni_p", "fs_ndv_p"});
+    typesMv.put(NumberType.DOUBLE, new String[]{"tds", "tds_dv", "tds_ni_dv", "ds_p", "ds_ni_p", "ds_ndv_p"});
+    typesMv.put(NumberType.DATE, new String[]{"tdts", "tdts_dv", "tdts_ni_dv", "dts_p", "dts_ni_p", "dts_ndv_p"});
+
+    for (int i = 0; i < atLeast(500); i++) {
+      if (random().nextInt(50) == 0) {
+        //have some empty docs
+        assertU(adoc("id", String.valueOf(i)));
+        continue;
+      }
+
+      if (random().nextInt(100) == 0 && i > 0) {
+        //delete some docs
+        assertU(delI(String.valueOf(i - 1)));
+      }
+      SolrInputDocument document = new SolrInputDocument();
+      document.setField("id", i);
+      for (Map.Entry<NumberType,String[]> entry:types.entrySet()) {
+        NumberType type = entry.getKey();
+        String val = null;
+        List<String> vals = null;
+        switch (type) {
+          case DATE:
+            val = randomDate(cardinality);
+            vals = getRandomDates(random().nextInt(10), cardinality);
+            break;
+          case DOUBLE:
+            val = String.valueOf(randomDouble(cardinality));
+            vals = toStringList(getRandomDoubles(random().nextInt(10), cardinality));
+            break;
+          case FLOAT:
+            val = String.valueOf(randomFloat(cardinality));
+            vals = toStringList(getRandomFloats(random().nextInt(10), cardinality));
+            break;
+          case INTEGER:
+            val = String.valueOf(randomInt(cardinality));
+            vals = toStringList(getRandomInts(random().nextInt(10), cardinality));
+            break;
+          case LONG:
+            val = String.valueOf(randomLong(cardinality));
+            vals = toStringList(getRandomLongs(random().nextInt(10), cardinality));
+            break;
+          default:
+            throw new AssertionError();
+
+        }
+        // SingleValue
+        for (String fieldSuffix:entry.getValue()) {
+          document.setField("field_sv_" + fieldSuffix, val);
+        }
+        //  MultiValue
+        for (String fieldSuffix:typesMv.get(type)) {
+          for (String value:vals) {
+            document.addField("field_mv_" + fieldSuffix, value);
+          }
+        }
+      }
+
+      assertU(adoc(document));
+      if (random().nextInt(50) == 0) {
+        assertU(commit());
+      }
+    }
+    assertU(commit());
+
+    String[][] possibleTypes = new String[types.size()][];
+    types.values().toArray(possibleTypes);
+    String[][] possibleTypesMv = new String[typesMv.size()][];
+    typesMv.values().toArray(possibleTypesMv);
+    for (int i = 0; i < atLeast(1000); i++) {
+      doTestQuery(cardinality, false, pickRandom(possibleTypes));
+      doTestQuery(cardinality, true, pickRandom(possibleTypesMv));
+    }
+  }
+
+  private void doTestQuery(int cardinality, boolean mv, String[] types) throws Exception {
+    String[] startOptions = new String[]{"{", "["};
+    String[] endOptions = new String[]{"}", "]"};
+    String[] qRange = getRandomRange(cardinality, types[0]);
+    String start = pickRandom(startOptions);
+    String end = pickRandom(endOptions);
+    long expectedHits = doRangeQuery(mv, start, end, types[0], qRange);
+    for (int i = 1; i < types.length; i++) {
+      assertEquals("Unexpected results from query when comparing " + types[0] + " with " + types[i] + " and query: " +
+          start + qRange[0] + " TO " + qRange[1] + end + "\n", 
+          expectedHits, doRangeQuery(mv, start, end, types[i], qRange));
+    }
+  }
+
+  private long doRangeQuery(boolean mv, String start, String end, String field, String[] qRange) throws Exception {
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.set("q", "field_" + (mv?"mv_":"sv_") + field + ":" + start + qRange[0] + " TO " + qRange[1] + end);
+    SolrQueryRequest req = req(params);
+    try {
+      return (long) h.queryAndResponse("", req).getToLog().get("hits");
+    } finally {
+      req.close();
+    }
+
+  }
+
+  private String[] getRandomRange(int max, String fieldName) {
+    Number[] values = new Number[2];
+    FieldType ft = h.getCore().getLatestSchema().getField("field_" + fieldName).getType();
+    if (ft.getNumberType() == null) {
+      assert ft instanceof StrField;
+      values[0] = randomInt(max);
+      values[1] = randomInt(max);
+      Arrays.sort(values, (o1, o2) -> String.valueOf(o1).compareTo(String.valueOf(o2)));
+    } else {
+      switch (ft.getNumberType()) {
+        case DOUBLE:
+          values[0] = randomDouble(max);
+          values[1] = randomDouble(max);
+          break;
+        case FLOAT:
+          values[0] = randomFloat(max);
+          values[1] = randomFloat(max);
+          break;
+        case INTEGER:
+          values[0] = randomInt(max);
+          values[1] = randomInt(max);
+          break;
+        case LONG:
+          values[0] = randomLong(max);
+          values[1] = randomLong(max);
+          break;
+        case DATE:
+          values[0] = randomMs(max);
+          values[1] = randomMs(max);
+          break;
+        default:
+          throw new AssertionError("Unexpected number type");
+
+      }
+      if (random().nextInt(100) >= 1) {// sometimes don't sort the values. Should result in 0 hits
+        Arrays.sort(values);
+      }
+    }
+    String[] stringValues = new String[2];
+    if (rarely()) {
+      stringValues[0] = "*";
+    } else {
+      if (ft.getNumberType() == NumberType.DATE) {
+        stringValues[0] = dateFormat.format(values[0]);
+      } else {
+        stringValues[0] = String.valueOf(values[0]);
+      }
+    }
+    if (rarely()) {
+      stringValues[1] = "*";
+    } else {
+      if (ft.getNumberType() == NumberType.DATE) {
+        stringValues[1] = dateFormat.format(values[1]);
+      } else {
+        stringValues[1] = String.valueOf(values[1]);
+      }
+    }
+    return stringValues;
+  }
+
+
+  // Helper methods
+  private String randomDate(int cardinality) {
+    return dateFormat.format(new Date(randomMs(cardinality)));
+  }
+
+  private List<String> getRandomDates(int numValues, int cardinality) {
+    List<String> vals = new ArrayList<>(numValues);
+    for (int i = 0; i < numValues;i++) {
+      vals.add(randomDate(cardinality));
+    }
+    return vals;
+  }
+
+  private List<Double> getRandomDoubles(int numValues, int cardinality) {
+    List<Double> vals = new ArrayList<>(numValues);
+    for (int i = 0; i < numValues;i++) {
+      vals.add(randomDouble(cardinality));
+    }
+    return vals;
+  }
+  
+  private List<Float> getRandomFloats(int numValues, int cardinality) {
+    List<Float> vals = new ArrayList<>(numValues);
+    for (int i = 0; i < numValues;i++) {
+      vals.add(randomFloat(cardinality));
+    }
+    return vals;
+  }
+  
+  private List<Integer> getRandomInts(int numValues, int cardinality) {
+    List<Integer> vals = new ArrayList<>(numValues);
+    for (int i = 0; i < numValues;i++) {
+      vals.add(randomInt(cardinality));
+    }
+    return vals;
+  }
+  
+  private List<Long> getRandomLongs(int numValues, int cardinality) {
+    List<Long> vals = new ArrayList<>(numValues);
+    for (int i = 0; i < numValues;i++) {
+      vals.add(randomLong(cardinality));
+    }
+    return vals;
+  }
+
+  <T> List<String> toStringList(List<T> input) {
+    List<String> newList = new ArrayList<>(input.size());
+    for (T element:input) {
+      newList.add(String.valueOf(element));
+    }
+    return newList;
+  }
+
+  long randomMs(int cardinality) {
+    return DATE_START_TIME_RANDOM_TEST + random().nextInt(cardinality) * 1000 * (random().nextBoolean()?1:-1);
+  }
+
+  double randomDouble(int cardinality) {
+    if (rarely()) {
+      int num = random().nextInt(8);
+      if (num == 0) return Double.NEGATIVE_INFINITY;
+      if (num == 1) return Double.POSITIVE_INFINITY;
+      if (num == 2) return Double.MIN_VALUE;
+      if (num == 3) return Double.MAX_VALUE;
+      if (num == 4) return -Double.MIN_VALUE;
+      if (num == 5) return -Double.MAX_VALUE;
+      if (num == 6) return 0.0d;
+      if (num == 7) return -0.0d;
+    }
+    Double d = Double.NaN;
+    while (d.isNaN()) {
+      d = random().nextDouble();
+    }
+    return d * cardinality * (random().nextBoolean()?1:-1);
+  }
+
+  float randomFloat(int cardinality) {
+    if (rarely()) {
+      int num = random().nextInt(8);
+      if (num == 0) return Float.NEGATIVE_INFINITY;
+      if (num == 1) return Float.POSITIVE_INFINITY;
+      if (num == 2) return Float.MIN_VALUE;
+      if (num == 3) return Float.MAX_VALUE;
+      if (num == 4) return -Float.MIN_VALUE;
+      if (num == 5) return -Float.MAX_VALUE;
+      if (num == 6) return 0.0f;
+      if (num == 7) return -0.0f;
+    }
+    Float f = Float.NaN;
+    while (f.isNaN()) {
+      f = random().nextFloat();
+    }
+    return f * cardinality * (random().nextBoolean()?1:-1);
+  }
+
+  int randomInt(int cardinality) {
+    if (rarely()) {
+      int num = random().nextInt(2);
+      if (num == 0) return Integer.MAX_VALUE;
+      if (num == 1) return Integer.MIN_VALUE;
+    }
+    return random().nextInt(cardinality) * (random().nextBoolean()?1:-1);
+  }
+
+  long randomLong(int cardinality) {
+    if (rarely()) {
+      int num = random().nextInt(2);
+      if (num == 0) return Long.MAX_VALUE;
+      if (num == 1) return Long.MIN_VALUE;
+    }
+    return randomInt(cardinality);
+  }
+
   static boolean sameDocs(String msg, DocSet a, DocSet b) {
     DocIterator i = a.iterator();
     // System.out.println("SIZES="+a.size() + "," + b.size());