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 2016/10/11 15:52:27 UTC

[1/2] lucene-solr:jira/solr-8396: Patch from 29/Sep/16

Repository: lucene-solr
Updated Branches:
  refs/heads/jira/solr-8396 [created] bc9041354


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bc904135/solr/core/src/test/org/apache/solr/TestPointFields.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/TestPointFields.java b/solr/core/src/test/org/apache/solr/TestPointFields.java
new file mode 100644
index 0000000..1636b8b
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/TestPointFields.java
@@ -0,0 +1,1075 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr;
+
+import java.lang.invoke.MethodHandles;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.schema.DoublePointField;
+import org.apache.solr.schema.IntPointField;
+import org.apache.solr.schema.PointField;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests for PointField functionality
+ *
+ *
+ */
+public class TestPointFields extends SolrTestCaseJ4 {
+  
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+  
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig.xml","schema-point.xml");
+  }
+  
+  @Override
+  @After
+  public void tearDown() throws Exception {
+    clearIndex();
+    assertU(commit());
+    super.tearDown();
+  }
+  
+  @Test
+  public void testIntPointFieldExactQuery() throws Exception {
+    doTestIntPointFieldExactQuery("number_p_i");
+    doTestIntPointFieldExactQuery("number_p_i_mv");
+    doTestIntPointFieldExactQuery("number_p_i_ni_dv");
+    doTestIntPointFieldExactQuery("number_p_i_ni_mv_dv");
+    
+  }
+  
+  private void doTestIntPointFieldExactQuery(String field) throws Exception {
+    for (int i=0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), field, String.valueOf(i+1)));
+    }
+    assertU(commit());
+    for (int i = 0; i < 10; i++) {
+      assertQ(req("q", field + ":"+(i+1), "fl", "id, " + field), 
+          "//*[@numFound='1']");
+    }
+    
+    for (int i = 0; i < 10; i++) {
+      assertQ(req("q", field + ":" + (i+1) + " OR " + field + ":" + ((i+1)%10 + 1)), "//*[@numFound='2']");
+    }
+    clearIndex();
+    assertU(commit());
+  }
+  
+  @Test
+  public void testIntPointFieldReturn() throws Exception {
+    testPointFieldReturn("number_p_i", "int", new String[]{"0", "1", "2", "3", "43", "52", "60", "74", "80", "99"});
+  }
+  
+  private void testPointFieldReturn(String field, String type, String[] values) throws Exception {
+    for (int i=0; i < values.length; i++) {
+      assertU(adoc("id", String.valueOf(i), field, values[i]));
+    }
+    assertU(commit());
+    String[] expected = new String[values.length + 1];
+    expected[0] = "//*[@numFound='" + values.length + "']"; 
+    for (int i = 1; i <= values.length; i++) {
+      expected[i] = "//result/doc[" + i + "]/" + type + "[@name='" + field + "'][.='" + values[i-1] + "']";
+    }
+    assertQ(req("q", "*:*", "fl", "id, " + field, "rows", String.valueOf(values.length)), expected);
+  }
+  
+  @Test
+  public void testIntPointFieldRangeQuery() throws Exception {
+    String fieldName = "number_p_i";
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), fieldName, String.valueOf(i)));
+    }
+    assertU(commit());
+    assertQ(req("q", fieldName + ":[0 TO 3]", "fl", "id, " + fieldName), 
+        "//*[@numFound='4']",
+        "//result/doc[1]/int[@name='" + fieldName + "'][.='0']",
+        "//result/doc[2]/int[@name='" + fieldName + "'][.='1']",
+        "//result/doc[3]/int[@name='" + fieldName + "'][.='2']",
+        "//result/doc[4]/int[@name='" + fieldName + "'][.='3']");
+    
+    assertQ(req("q", fieldName + ":{0 TO 3]", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/int[@name='" + fieldName + "'][.='1']",
+        "//result/doc[2]/int[@name='" + fieldName + "'][.='2']",
+        "//result/doc[3]/int[@name='" + fieldName + "'][.='3']");
+    
+    assertQ(req("q", fieldName + ":[0 TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/int[@name='" + fieldName + "'][.='0']",
+        "//result/doc[2]/int[@name='" + fieldName + "'][.='1']",
+        "//result/doc[3]/int[@name='" + fieldName + "'][.='2']");
+    
+    assertQ(req("q", fieldName + ":{0 TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='2']",
+        "//result/doc[1]/int[@name='" + fieldName + "'][.='1']",
+        "//result/doc[2]/int[@name='" + fieldName + "'][.='2']");
+    
+    assertQ(req("q", fieldName + ":{0 TO *}", "fl", "id, " + fieldName), 
+        "//*[@numFound='9']",
+        "//result/doc[1]/int[@name='" + fieldName + "'][.='1']");
+    
+    assertQ(req("q", fieldName + ":{* TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/int[@name='" + fieldName + "'][.='0']");
+    
+    assertQ(req("q", fieldName + ":[* TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/int[@name='" + fieldName + "'][.='0']");
+    
+    assertQ(req("q", fieldName + ":[* TO *}", "fl", "id, " + fieldName), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/int[@name='" + fieldName + "'][.='0']",
+        "//result/doc[10]/int[@name='" + fieldName + "'][.='9']");
+    
+    clearIndex();
+    assertU(commit());
+    
+    String[] arr = getRandomStringArrayWithInts(10, true);
+    for (int i = 0; i < arr.length; i++) {
+      assertU(adoc("id", String.valueOf(i), fieldName, arr[i]));
+    }
+    assertU(commit());
+    for (int i = 0; i < arr.length; i++) {
+      assertQ(req("q", fieldName + ":[" + arr[0] + " TO " + arr[i] + "]", "fl", "id, " + fieldName), 
+          "//*[@numFound='" + (i + 1) + "']");
+      assertQ(req("q", fieldName + ":{" + arr[0] + " TO " + arr[i] + "}", "fl", "id, " + fieldName), 
+          "//*[@numFound='" + (Math.max(0,  i-1)) + "']");
+    }
+  }
+
+  @Test
+  public void testIntPointFieldSort() throws Exception {
+    testPointFieldSort("number_p_i", "number_p_i_dv", new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"});
+  }
+  
+  private void testPointFieldFacetField(String nonDocValuesField, String docValuesField, String[] numbers) throws Exception {
+    assert numbers != null && numbers.length == 10;
+    
+    assertFalse(h.getCore().getLatestSchema().getField(docValuesField).multiValued());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
+    
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), docValuesField, numbers[i], nonDocValuesField, numbers[i]));
+    }
+    assertU(commit());
+    assertQ(req("q", "*:*", "fl", "id, " + docValuesField, "facet", "true", "facet.field", docValuesField), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[1] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[2] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[3] + "'][.='1']");
+    
+    assertU(adoc("id", "10", docValuesField, numbers[1], nonDocValuesField, numbers[1]));
+    
+    assertU(commit());
+    assertQ(req("q", "*:*", "fl", "id, " + docValuesField, "facet", "true", "facet.field", docValuesField), 
+        "//*[@numFound='11']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[1] + "'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[2] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[3] + "'][.='1']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
+    assertQEx("Expecting Exception", 
+        "Can't facet on a PointField without docValues", 
+        req("q", "*:*", "fl", "id, " + nonDocValuesField, "facet", "true", "facet.field", nonDocValuesField), 
+        SolrException.ErrorCode.BAD_REQUEST);
+  }
+  
+  @Test
+  public void testIntPointFieldFacetField() throws Exception {
+    testPointFieldFacetField("number_p_i", "number_p_i_dv", getSequentialStringArrayWithInts(10));
+  }
+  
+  @Test
+  public void testIntPointFieldRangeFacet() throws Exception {
+    String docValuesField = "number_p_i_dv";
+    String nonDocValuesField = "number_p_i";
+    
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), docValuesField, String.valueOf(i), nonDocValuesField, String.valueOf(i)));
+    }
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof IntPointField);
+    assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", "-10", "facet.range.end", "10", "facet.range.gap", "2"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='2'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='4'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='6'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='8'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='-10'][.='0']");
+    
+    assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", "-10", "facet.range.end", "10", "facet.range.gap", "2", "facet.range.method", "dv"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='2'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='4'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='6'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='8'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='-10'][.='0']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof IntPointField);
+    // Range Faceting with method = filter should work
+    assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", "-10", "facet.range.end", "10", "facet.range.gap", "2", "facet.range.method", "filter"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='2'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='4'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='6'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='8'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='-10'][.='0']");
+    
+    // this should actually use filter method instead of dv
+    assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", "-10", "facet.range.end", "10", "facet.range.gap", "2", "facet.range.method", "dv"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='2'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='4'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='6'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='8'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='-10'][.='0']");
+  }
+
+  @Test
+  public void testIntPointFunctionQuery() throws Exception {
+    String dvFieldName = "number_p_i_dv";
+    String nonDvFieldName = "number_p_i";
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), dvFieldName, String.valueOf(i), nonDvFieldName, String.valueOf(i)));
+    }
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(dvFieldName).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(dvFieldName).getType() instanceof IntPointField);
+    assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "sort", "product(-1," + dvFieldName + ") asc"), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/int[@name='" + dvFieldName + "'][.='9']",
+        "//result/doc[2]/int[@name='" + dvFieldName + "'][.='8']",
+        "//result/doc[3]/int[@name='" + dvFieldName + "'][.='7']",
+        "//result/doc[10]/int[@name='" + dvFieldName + "'][.='0']");
+    
+    assertQ(req("q", "*:*", "fl", "id, " + dvFieldName + ", product(-1," + dvFieldName + ")"), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/float[@name='product(-1," + dvFieldName + ")'][.='-0.0']",
+        "//result/doc[2]/float[@name='product(-1," + dvFieldName + ")'][.='-1.0']",
+        "//result/doc[3]/float[@name='product(-1," + dvFieldName + ")'][.='-2.0']",
+        "//result/doc[10]/float[@name='product(-1," + dvFieldName + ")'][.='-9.0']");
+    
+    assertQ(req("q", "*:*", "fl", "id, " + dvFieldName + ", field(" + dvFieldName + ")"), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/int[@name='field(" + dvFieldName + ")'][.='0']",
+        "//result/doc[2]/int[@name='field(" + dvFieldName + ")'][.='1']",
+        "//result/doc[3]/int[@name='field(" + dvFieldName + ")'][.='2']",
+        "//result/doc[10]/int[@name='field(" + dvFieldName + ")'][.='9']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDvFieldName).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDvFieldName).getType() instanceof IntPointField);
+
+    assertQEx("Expecting Exception", 
+        "sort param could not be parsed as a query", 
+        req("q", "*:*", "fl", "id, " + nonDvFieldName, "sort", "product(-1," + nonDvFieldName + ") asc"), 
+        SolrException.ErrorCode.BAD_REQUEST);
+  }
+  
+  private void testPointStats(String field, String dvField, String[] numbers, String min, String max, String count, String missing) {
+    for (int i = 0; i < numbers.length; i++) {
+      assertU(adoc("id", String.valueOf(i), dvField, numbers[i], field, numbers[i]));
+    }
+    assertU(adoc("id", String.valueOf(numbers.length)));
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(dvField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(dvField).getType() instanceof PointField);
+    assertQ(req("q", "*:*", "fl", "id, " + dvField, "stats", "true", "stats.field", dvField), 
+        "//*[@numFound='11']",
+        "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField+ "']/double[@name='min'][.='" + min + "']",
+        "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField+ "']/double[@name='max'][.='" + max + "']",
+        "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField+ "']/long[@name='count'][.='" + count + "']",
+        "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField+ "']/long[@name='missing'][.='" + missing + "']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(field).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
+    assertQEx("Expecting Exception", 
+        "Can't calculate stats on a PointField without docValues", 
+        req("q", "*:*", "fl", "id, " + field, "stats", "true", "stats.field", field), 
+        SolrException.ErrorCode.BAD_REQUEST);
+  }
+
+  @Test
+  public void testIntPointStats() throws Exception {
+    testPointStats("number_p_i", "number_p_i_dv", new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"},
+        "0.0", "9.0", "10", "1");
+  }
+  
+  @Test
+  public void testIntPointGrouping() throws Exception {
+    
+  }
+  
+  @Test
+  public void testIntPointPivotFaceting() throws Exception {
+    
+  }
+  
+  private void testPointFieldMultiValuedExactQuery(String fieldName, 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);
+    for (int i=0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), fieldName, numbers[i], fieldName, numbers[i+10]));
+    }
+    assertU(commit());
+    for (int i = 0; i < 20; i++) {
+      assertQ(req("q", fieldName + ":" + numbers[i].replace("-", "\\-")), 
+          "//*[@numFound='1']");
+    }
+    
+    for (int i = 0; i < 20; i++) {
+      assertQ(req("q", fieldName + ":" + numbers[i].replace("-", "\\-") + " OR " + fieldName + ":" + numbers[(i+1)%10].replace("-", "\\-")), "//*[@numFound='2']");
+    }
+  }
+
+  @Test
+  public void testIntPointFieldMultiValuedExactQuery() throws Exception {
+    testPointFieldMultiValuedExactQuery("number_p_i_mv", getSequentialStringArrayWithInts(20));
+  }
+  
+  private void testPointFieldMultiValuedReturn(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);
+    for (int i=0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), fieldName, numbers[i], fieldName, numbers[i+10]));
+    }
+    assertU(commit());
+    String[] expected = new String[11];
+    String[] expected2 = new String[11];
+    expected[0] = "//*[@numFound='10']"; 
+    expected2[0] = "//*[@numFound='10']"; 
+    for (int i = 1; i <= 10; i++) {
+      expected[i] = "//result/doc[" + i + "]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[i-1] + "']";
+      expected2[i] = "//result/doc[" + i + "]/arr[@name='" + fieldName + "']/" + type + "[2][.='" + numbers[i + 9] + "']";
+    }
+    assertQ(req("q", "*:*", "fl", "id, " + fieldName), expected);
+    assertQ(req("q", "*:*", "fl", "id, " + fieldName), expected2);
+  }
+  
+  @Test
+  public void testIntPointFieldMultiValuedReturn() throws Exception {
+    testPointFieldMultiValuedReturn("number_p_i_mv", "int", getSequentialStringArrayWithInts(20));
+  }
+  
+  private void testPointFieldMultiValuedRangeQuery(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);
+    for (int i=0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), fieldName, String.valueOf(i), fieldName, String.valueOf(i+10)));
+    }
+    assertU(commit());
+    assertQ(req("q", fieldName + ":[0 TO 3]", "fl", "id, " + fieldName), 
+        "//*[@numFound='4']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[2][.='" + numbers[10] + "']",
+        "//result/doc[2]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[1] + "']",
+        "//result/doc[2]/arr[@name='" + fieldName + "']/" + type + "[2][.='" + numbers[11] + "']",
+        "//result/doc[3]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[2] + "']",
+        "//result/doc[3]/arr[@name='" + fieldName + "']/" + type + "[2][.='" + numbers[12] + "']",
+        "//result/doc[4]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[3] + "']",
+        "//result/doc[4]/arr[@name='" + fieldName + "']/" + type + "[2][.='" + numbers[13] + "']");
+    
+    assertQ(req("q", fieldName + ":{0 TO 3]", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[1] + "']",
+        "//result/doc[2]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[2] + "']",
+        "//result/doc[3]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[3] + "']");
+    
+    assertQ(req("q", fieldName + ":[0 TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']",
+        "//result/doc[2]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[1] + "']",
+        "//result/doc[3]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[2] + "']");
+    
+    assertQ(req("q", fieldName + ":{0 TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='2']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[1] + "']",
+        "//result/doc[2]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[2] + "']");
+    
+    assertQ(req("q", fieldName + ":{0 TO *}", "fl", "id, " + fieldName), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']");
+    
+    assertQ(req("q", fieldName + ":{10 TO *}", "fl", "id, " + fieldName), 
+        "//*[@numFound='9']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[1] + "']");
+    
+    assertQ(req("q", fieldName + ":{* TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']");
+    
+    assertQ(req("q", fieldName + ":[* TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']");
+    
+    assertQ(req("q", fieldName + ":[* TO *}", "fl", "id, " + fieldName), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']",
+        "//result/doc[10]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[9] + "']");
+  }
+  
+  @Test
+  public void testIntPointFieldMultiValuedRangeQuery() throws Exception {
+    testPointFieldMultiValuedRangeQuery("number_p_i_mv", "int", getSequentialStringArrayWithInts(20));
+  }
+  
+  //TODO MV SORT?
+  
+  private void testPointFieldMultiValuedFacetField(String nonDocValuesField, String dvFieldName, String[] numbers) throws Exception {
+    assert numbers != null && numbers.length == 20;
+    assertTrue(h.getCore().getLatestSchema().getField(dvFieldName).multiValued());
+    assertTrue(h.getCore().getLatestSchema().getField(dvFieldName).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(dvFieldName).getType() instanceof PointField);
+    
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), dvFieldName, numbers[i], dvFieldName, numbers[i + 10], 
+          nonDocValuesField, numbers[i], nonDocValuesField, numbers[i + 10]));
+    }
+    assertU(commit());
+    
+    assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "facet", "true", "facet.field", dvFieldName), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[1] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[2] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[3] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[10] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[11] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[12] + "'][.='1']");
+    
+    assertU(adoc("id", "10", dvFieldName, numbers[1], nonDocValuesField, numbers[1]));
+    
+    assertU(commit());
+    assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "facet", "true", "facet.field", dvFieldName), 
+        "//*[@numFound='11']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[1] + "'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[2] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[3] + "'][.='1']",
+        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName +"']/int[@name='" + numbers[10] + "'][.='1']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
+    assertQEx("Expecting Exception", 
+        "Can't facet on a PointField without docValues", 
+        req("q", "*:*", "fl", "id, " + nonDocValuesField, "facet", "true", "facet.field", nonDocValuesField), 
+        SolrException.ErrorCode.BAD_REQUEST);
+  }
+  
+
+  @Test
+  public void testIntPointFieldMultiValuedFacetField() throws Exception {
+    testPointFieldMultiValuedFacetField("number_p_i_mv", "number_p_i_mv_dv", getSequentialStringArrayWithInts(20));
+  }
+  
+
+  @Test
+  public void testIntPointFieldMultiValuedRangeFacet() throws Exception {
+    String docValuesField = "number_p_i_mv_dv";
+    String nonDocValuesField = "number_p_i_mv";
+    
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), docValuesField, String.valueOf(i), docValuesField, String.valueOf(i + 10), 
+          nonDocValuesField, String.valueOf(i), nonDocValuesField, String.valueOf(i + 10)));
+    }
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof IntPointField);
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start", "-10", "facet.range.end", "20", "facet.range.gap", "2"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='2'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='4'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='6'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='8'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='10'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='12'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='14'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='16'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='18'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='-10'][.='0']");
+    
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start", "-10", "facet.range.end", "20", "facet.range.gap", "2", "facet.range.method", "dv"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='2'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='4'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='6'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='8'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='10'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='12'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='14'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='16'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='18'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='-10'][.='0']");
+    
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start", "0", "facet.range.end", "20", "facet.range.gap", "100"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0'][.='10']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof IntPointField);
+    // Range Faceting with method = filter should work
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", "-10", "facet.range.end", "20", "facet.range.gap", "2", "facet.range.method", "filter"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='2'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='4'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='6'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='8'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='10'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='12'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='14'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='16'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='18'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='-10'][.='0']");
+    
+    // this should actually use filter method instead of dv
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", "-10", "facet.range.end", "20", "facet.range.gap", "2", "facet.range.method", "dv"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='2'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='4'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='6'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='8'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='10'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='12'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='14'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='16'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='18'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='-10'][.='0']");
+  }
+  
+
+  private void testPointMultiValuedFunctionQuery(String nonDocValuesField, String docValuesField, String type, String[] numbers) throws Exception {
+    assert numbers != null && numbers.length == 20;
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), docValuesField, numbers[i], docValuesField, numbers[i+10], 
+          nonDocValuesField, numbers[i], nonDocValuesField, numbers[i+10]));
+    }
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).multiValued());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
+    String function = "field(" + docValuesField + ", min)";
+    
+    assertQ(req("q", "*:*", "fl", "id, " + function), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/" + type + "[@name='" + function + "'][.='" + numbers[0] + "']",
+        "//result/doc[2]/" + type + "[@name='" + function + "'][.='" + numbers[1] + "']",
+        "//result/doc[3]/" + type + "[@name='" + function + "'][.='" + numbers[2] + "']",
+        "//result/doc[10]/" + type + "[@name='" + function + "'][.='" + numbers[9] + "']");
+    
+//    if (dvIsRandomAccessOrds(docValuesField)) {
+//      function = "field(" + docValuesField + ", max)";
+//      assertQ(req("q", "*:*", "fl", "id, " + function), 
+//          "//*[@numFound='10']",
+//          "//result/doc[1]/int[@name='" + function + "'][.='10']",
+//          "//result/doc[2]/int[@name='" + function + "'][.='11']",
+//          "//result/doc[3]/int[@name='" + function + "'][.='12']",
+//          "//result/doc[10]/int[@name='" + function + "'][.='19']");
+//    }
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).multiValued());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
+
+    function = "field(" + nonDocValuesField + ",min)";
+    
+    assertQEx("Expecting Exception", 
+        "sort param could not be parsed as a query", 
+        req("q", "*:*", "fl", "id", "sort", function + " desc"), 
+        SolrException.ErrorCode.BAD_REQUEST);
+    
+    assertQEx("Expecting Exception", 
+        "docValues='true' is required to select 'min' value from multivalued field (" + nonDocValuesField + ") at query time", 
+        req("q", "*:*", "fl", "id, " + function), 
+        SolrException.ErrorCode.BAD_REQUEST);
+    
+    function = "field(" + docValuesField + ",foo)";
+    assertQEx("Expecting Exception", 
+        "Multi-Valued field selector 'foo' not supported", 
+        req("q", "*:*", "fl", "id, " + function), 
+        SolrException.ErrorCode.BAD_REQUEST);
+  }
+  
+  
+//  private boolean dvIsRandomAccessOrds(String field) throws IOException {
+//    RefCounted<SolrIndexSearcher> ref = null;
+//    try {
+//      ref = h.getCore().getSearcher(); 
+//      SortedSetDocValues values = DocValues.getSortedSet(ref.get().getIndexReader().leaves().get(0).reader(), field);
+//      return values instanceof RandomAccessOrds;
+//    } finally {
+//      if (ref != null) ref.decref();
+//    }
+//  }
+  
+  @Test
+  public void testIntPointMultiValuedFunctionQuery() throws Exception {
+    testPointMultiValuedFunctionQuery("number_p_i_mv", "number_p_i_mv_dv", "int", getSequentialStringArrayWithInts(20));
+  }
+  
+
+  @Test
+  public void testDoublePointFieldExactQuery() throws Exception {
+    doTestFloatPointFieldExactQuery("number_d");
+    doTestFloatPointFieldExactQuery("number_p_d");
+    doTestFloatPointFieldExactQuery("number_p_d_mv");
+    doTestFloatPointFieldExactQuery("number_p_d_ni_dv");
+    doTestFloatPointFieldExactQuery("number_p_d_ni_mv_dv");
+  }
+  
+  private void doTestFloatPointFieldExactQuery(String field) throws Exception {
+    for (int i=0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), field, String.valueOf(i + "." + i)));
+    }
+    assertU(commit());
+    for (int i = 0; i < 9; i++) {
+      assertQ(req("q", field + ":"+(i+1) + "." + (i+1), "fl", "id, " + field), 
+          "//*[@numFound='1']");
+    }
+    
+    for (int i = 0; i < 9; i++) {
+      String num1 = (i+1) + "." + (i+1);
+      String num2 = ((i+1)%9 + 1) + "." + ((i+1)%9 + 1);
+      assertQ(req("q", field + ":" + num1 + " OR " + field + ":" + num2), "//*[@numFound='2']");
+    }
+    
+    clearIndex();
+    assertU(commit());
+    for (int i = 0; i < atLeast(10); i++) {
+      float rand = random().nextFloat() * 10;
+      assertU(adoc("id", "random_number ", field, String.valueOf(rand))); //always the same id to override
+      assertU(commit());
+      assertQ(req("q", field + ":" + rand, "fl", "id, " + field), 
+          "//*[@numFound='1']");
+    }
+    clearIndex();
+    assertU(commit());
+  }
+  
+  @Test
+  public void testDoublePointFieldReturn() throws Exception {
+    testPointFieldReturn("number_p_d", "double", new String[]{"0.0", "1.2", "2.5", "3.02", "0.43", "5.2", "6.01", "74.0", "80.0", "9.9"});
+    clearIndex();
+    assertU(commit());
+    String[] arr = new String[atLeast(10)];
+    for (int i = 0; i < arr.length; i++) {
+      double rand = random().nextDouble() * 10;
+      arr[i] = String.valueOf(rand);
+    }
+    testPointFieldReturn("number_p_d", "double", arr);
+  }
+  
+  @Test
+  public void testDoublePointFieldRangeQuery() throws Exception {
+    String fieldName = "number_p_d";
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), fieldName, String.valueOf(i)));
+    }
+    assertU(commit());
+    assertQ(req("q", fieldName + ":[0 TO 3]", "fl", "id, " + fieldName), 
+        "//*[@numFound='4']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='0.0']",
+        "//result/doc[2]/double[@name='" + fieldName + "'][.='1.0']",
+        "//result/doc[3]/double[@name='" + fieldName + "'][.='2.0']",
+        "//result/doc[4]/double[@name='" + fieldName + "'][.='3.0']");
+    
+    assertQ(req("q", fieldName + ":{0 TO 3]", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='1.0']",
+        "//result/doc[2]/double[@name='" + fieldName + "'][.='2.0']",
+        "//result/doc[3]/double[@name='" + fieldName + "'][.='3.0']");
+    
+    assertQ(req("q", fieldName + ":[0 TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='0.0']",
+        "//result/doc[2]/double[@name='" + fieldName + "'][.='1.0']",
+        "//result/doc[3]/double[@name='" + fieldName + "'][.='2.0']");
+    
+    assertQ(req("q", fieldName + ":{0 TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='2']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='1.0']",
+        "//result/doc[2]/double[@name='" + fieldName + "'][.='2.0']");
+    
+    assertQ(req("q", fieldName + ":{0 TO *}", "fl", "id, " + fieldName), 
+        "//*[@numFound='9']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='1.0']");
+    
+    assertQ(req("q", fieldName + ":{* TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='0.0']");
+    
+    assertQ(req("q", fieldName + ":[* TO 3}", "fl", "id, " + fieldName), 
+        "//*[@numFound='3']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='0.0']");
+    
+    assertQ(req("q", fieldName + ":[* TO *}", "fl", "id, " + fieldName), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='0.0']",
+        "//result/doc[10]/double[@name='" + fieldName + "'][.='9.0']");
+    
+    assertQ(req("q", fieldName + ":[0.9 TO 1.01]", "fl", "id, " + fieldName), 
+        "//*[@numFound='1']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='1.0']");
+    
+    assertQ(req("q", fieldName + ":{0.9 TO 1.01}", "fl", "id, " + fieldName), 
+        "//*[@numFound='1']",
+        "//result/doc[1]/double[@name='" + fieldName + "'][.='1.0']");
+    
+    clearIndex();
+    assertU(commit());
+    
+    String[] arr = getRandomStringArrayWithFloats(10, true);
+    for (int i = 0; i < arr.length; i++) {
+      assertU(adoc("id", String.valueOf(i), fieldName, arr[i]));
+    }
+    assertU(commit());
+    for (int i = 0; i < arr.length; i++) {
+      assertQ(req("q", fieldName + ":[" + arr[0] + " TO " + arr[i] + "]", "fl", "id, " + fieldName), 
+          "//*[@numFound='" + (i + 1) + "']");
+      assertQ(req("q", fieldName + ":{" + arr[0] + " TO " + arr[i] + "}", "fl", "id, " + fieldName), 
+          "//*[@numFound='" + (Math.max(0,  i-1)) + "']");
+    }
+  }
+  
+  @Test
+  public void testDoublePointFieldSort() throws Exception {
+    String[] arr = getRandomStringArrayWithFloats(10, true);
+    testPointFieldSort("number_p_d", "number_p_d_dv", arr);
+  }
+  
+  private void testPointFieldSort(String field, String dvField, String[] arr) throws Exception {
+    assert arr != null && arr.length == 10;
+    for (int i = 0; i < arr.length; i++) {
+      assertU(adoc("id", String.valueOf(i), dvField, String.valueOf(arr[i]), field, String.valueOf(arr[i])));
+    }
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(dvField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(dvField).getType() instanceof PointField);
+    assertQ(req("q", "*:*", "fl", "id", "sort", dvField + " desc"), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/str[@name='id'][.='9']",
+        "//result/doc[2]/str[@name='id'][.='8']",
+        "//result/doc[3]/str[@name='id'][.='7']",
+        "//result/doc[10]/str[@name='id'][.='0']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(field).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
+    assertQEx("Expecting Exception", 
+        "can not sort on a PointField without doc values: " + field, 
+        req("q", "*:*", "fl", "id", "sort", field + " desc"), 
+        SolrException.ErrorCode.BAD_REQUEST);
+    
+    //TODO: sort missing
+  }
+  
+  @Test
+  public void testDoublePointFieldFacetField() throws Exception {
+//    testPointFieldFacetField("number_p_d", "number_p_d_dv", getSequentialStringArrayWithDoubles(10));
+    testPointFieldFacetField("number_p_d", "number_p_d_dv", getRandomStringArrayWithFloats(10, false));
+  }
+  
+  private String[] getRandomStringArrayWithFloats(int length, boolean sorted) {
+    Set<Float> set;
+    if (sorted) {
+      set = new TreeSet<>();
+    } else {
+      set = new HashSet<>();
+    }
+    while (set.size() < length) {
+      float f = random().nextFloat() * 100;
+      if (random().nextBoolean()) {
+        f = f * -1;
+      }
+      set.add(f);
+    }
+    String[] stringArr = new String[length];
+    int i = 0;
+    for (float val:set) {
+      stringArr[i] = String.valueOf(val);
+      i++;
+    }
+    return stringArr;
+  }
+  
+  private String[] getSequentialStringArrayWithInts(int length) {
+    String[] arr = new String[length];
+    for (int i = 0; i < length; i++) {
+      arr[i] = String.valueOf(i);
+    }
+    return arr;
+  }
+  
+  private String[] getSequentialStringArrayWithDoubles(int length) {
+    String[] arr = new String[length];
+    for (int i = 0; i < length; i++) {
+      arr[i] = String.format(Locale.ROOT, "%d.0", i);
+    }
+    return arr;
+  }
+  
+  private String[] getRandomStringArrayWithInts(int length, boolean sorted) {
+    Set<Integer> set;
+    if (sorted) {
+      set = new TreeSet<>();
+    } else {
+      set = new HashSet<>();
+    }
+    while (set.size() < length) {
+      int number = random().nextInt(100);
+      if (random().nextBoolean()) {
+        number = number * -1;
+      }
+      set.add(number);
+    }
+    String[] stringArr = new String[length];
+    int i = 0;
+    for (int val:set) {
+      stringArr[i] = String.valueOf(val);
+      i++;
+    }
+    return stringArr;
+  }
+
+  @Test
+  public void testDoublePointFieldRangeFacet() throws Exception {
+    String docValuesField = "number_p_d_dv";
+    String nonDocValuesField = "number_p_d";
+    
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), docValuesField, String.format(Locale.ROOT, "%f", (double)i*1.1), nonDocValuesField, String.format(Locale.ROOT, "%f", (double)i*1.1)));
+    }
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof DoublePointField);
+    assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", "-10", "facet.range.end", "10", "facet.range.gap", "2"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='2.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='4.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='6.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='8.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='-10.0'][.='0']");
+    
+    assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", "-10", "facet.range.end", "10", "facet.range.gap", "2", "facet.range.method", "dv"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='2.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='4.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='6.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='8.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='-10.0'][.='0']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof DoublePointField);
+    // Range Faceting with method = filter should work
+    assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", "-10", "facet.range.end", "10", "facet.range.gap", "2", "facet.range.method", "filter"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='2.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='4.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='6.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='8.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='-10.0'][.='0']");
+    
+    // this should actually use filter method instead of dv
+    assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", "-10", "facet.range.end", "10", "facet.range.gap", "2", "facet.range.method", "dv"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='2.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='4.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='6.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='8.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='-10.0'][.='0']");
+  }
+
+
+  @Test
+  public void testDoublePointFunctionQuery() throws Exception {
+    String dvFieldName = "number_p_d_dv";
+    String nonDvFieldName = "number_p_d";
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), dvFieldName, String.format(Locale.ROOT, "%f", (double)i*1.1), nonDvFieldName, String.format(Locale.ROOT, "%f", (double)i*1.1)));
+    }
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(dvFieldName).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(dvFieldName).getType() instanceof DoublePointField);
+    assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "sort", "product(-1," + dvFieldName + ") asc"), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/double[@name='" + dvFieldName + "'][.='9.9']",
+        "//result/doc[2]/double[@name='" + dvFieldName + "'][.='8.8']",
+        "//result/doc[3]/double[@name='" + dvFieldName + "'][.='7.7']",
+        "//result/doc[10]/double[@name='" + dvFieldName + "'][.='0.0']");
+    
+    assertQ(req("q", "*:*", "fl", "id, " + dvFieldName + ", product(-1," + dvFieldName + ")"), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/float[@name='product(-1," + dvFieldName + ")'][.='-0.0']",
+        "//result/doc[2]/float[@name='product(-1," + dvFieldName + ")'][.='-1.1']",
+        "//result/doc[3]/float[@name='product(-1," + dvFieldName + ")'][.='-2.2']",
+        "//result/doc[10]/float[@name='product(-1," + dvFieldName + ")'][.='-9.9']");
+    
+    assertQ(req("q", "*:*", "fl", "id, " + dvFieldName + ", field(" + dvFieldName + ")"), 
+        "//*[@numFound='10']",
+        "//result/doc[1]/double[@name='field(" + dvFieldName + ")'][.='0.0']",
+        "//result/doc[2]/double[@name='field(" + dvFieldName + ")'][.='1.1']",
+        "//result/doc[3]/double[@name='field(" + dvFieldName + ")'][.='2.2']",
+        "//result/doc[10]/double[@name='field(" + dvFieldName + ")'][.='9.9']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDvFieldName).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDvFieldName).getType() instanceof DoublePointField);
+
+    assertQEx("Expecting Exception", 
+        "sort param could not be parsed as a query", 
+        req("q", "*:*", "fl", "id, " + nonDvFieldName, "sort", "product(-1," + nonDvFieldName + ") asc"), 
+        SolrException.ErrorCode.BAD_REQUEST);
+  }
+  
+  @Test
+  public void testDoublePointStats() throws Exception {
+    testPointStats("number_p_d", "number_p_d_dv", new String[]{"-10.0", "1.1", "2.2", "3.3", "4.4", "5.5", "6.6", "7.7", "8.8", "9.9"},
+        "-10.0", "9.9", "10", "1");
+  }
+  
+  @Test
+  public void testDoublePointFieldMultiValuedExactQuery() throws Exception {
+    testPointFieldMultiValuedExactQuery("number_p_d_mv", getRandomStringArrayWithFloats(20, false));
+  }
+  
+  @Test
+  public void testDoublePointFieldMultiValuedReturn() throws Exception {
+    testPointFieldMultiValuedReturn("number_p_d_mv", "double", getSequentialStringArrayWithDoubles(20));
+  }
+  
+  @Test
+  public void testDoublePointFieldMultiValuedRangeQuery() throws Exception {
+    testPointFieldMultiValuedRangeQuery("number_p_d_mv", "double", getSequentialStringArrayWithDoubles(20));
+  }
+  
+  @Test
+  public void testDoublePointFieldMultiValuedFacetField() throws Exception {
+//    testPointFieldMultiValuedFacetField("number_p_d_mv", "number_p_d_mv_dv", getSequentialStringArrayWithDoubles(20));
+    testPointFieldMultiValuedFacetField("number_p_d_mv", "number_p_d_mv_dv", getRandomStringArrayWithFloats(20, false));
+  }
+  
+
+  @Test
+  public void testDoublePointFieldMultiValuedRangeFacet() throws Exception {
+    String docValuesField = "number_p_d_mv_dv";
+    String nonDocValuesField = "number_p_d_mv";
+    
+    for (int i = 0; i < 10; i++) {
+      assertU(adoc("id", String.valueOf(i), docValuesField, String.valueOf(i), docValuesField, String.valueOf(i + 10), 
+          nonDocValuesField, String.valueOf(i), nonDocValuesField, String.valueOf(i + 10)));
+    }
+    assertU(commit());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).multiValued());
+    assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof DoublePointField);
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start", "-10", "facet.range.end", "20", "facet.range.gap", "2"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='2.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='4.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='6.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='8.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='10.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='12.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='14.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='16.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='18.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='-10.0'][.='0']");
+    
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start", "-10", "facet.range.end", "20", "facet.range.gap", "2", "facet.range.method", "dv"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='2.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='4.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='6.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='8.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='10.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='12.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='14.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='16.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='18.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='-10.0'][.='0']");
+    
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start", "0", "facet.range.end", "20", "facet.range.gap", "100"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='10']");
+    
+    assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).multiValued());
+    assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof DoublePointField);
+    // Range Faceting with method = filter should work
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", "-10", "facet.range.end", "20", "facet.range.gap", "2", "facet.range.method", "filter"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='2.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='4.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='6.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='8.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='10.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='12.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='14.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='16.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='18.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='-10.0'][.='0']");
+    
+    // this should actually use filter method instead of dv
+    assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", "-10", "facet.range.end", "20", "facet.range.gap", "2", "facet.range.method", "dv"), 
+        "//*[@numFound='10']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='0.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='2.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='4.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='6.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='8.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='10.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='12.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='14.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='16.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='18.0'][.='2']",
+        "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField + "']/lst[@name='counts']/int[@name='-10.0'][.='0']");
+  }
+  
+  @Test
+  public void testDoublePointMultiValuedFunctionQuery() throws Exception {
+//    testPointMultiValuedFunctionQuery("number_p_d_mv", "number_p_d_mv_dv", "double", getSequentialStringArrayWithDoubles(20));
+    testPointMultiValuedFunctionQuery("number_p_d_mv", "number_p_d_mv_dv", "double", getRandomStringArrayWithFloats(20, true));
+  }
+  
+}


[2/2] lucene-solr:jira/solr-8396: Patch from 29/Sep/16

Posted by tf...@apache.org.
Patch from 29/Sep/16


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

Branch: refs/heads/jira/solr-8396
Commit: bc904135497ad61724d0f9e386aa7ed34b2c6153
Parents: 8648fd5
Author: Tomas Fernandez Lobbe <tf...@apache.org>
Authored: Tue Oct 11 08:51:41 2016 -0700
Committer: Tomas Fernandez Lobbe <tf...@apache.org>
Committed: Tue Oct 11 08:51:41 2016 -0700

----------------------------------------------------------------------
 .../apache/solr/schema/DoublePointField.java    |  243 ++++
 .../org/apache/solr/schema/IntPointField.java   |  258 +++++
 .../java/org/apache/solr/schema/PointField.java |  254 +++++
 .../solr/collection1/conf/schema-point.xml      |  123 ++
 .../test/org/apache/solr/TestPointFields.java   | 1075 ++++++++++++++++++
 5 files changed, 1953 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bc904135/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
new file mode 100644
index 0000000..c01728b
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.schema;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Map;
+
+import org.apache.lucene.document.DoublePoint;
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.legacy.LegacyNumericType;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
+import org.apache.lucene.queries.function.valuesource.DoubleFieldSource;
+import org.apache.lucene.queries.function.valuesource.SortedSetFieldSource;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.SortedSetSelector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.util.mutable.MutableValue;
+import org.apache.lucene.util.mutable.MutableValueDouble;
+import org.apache.solr.search.QParser;
+import org.apache.solr.uninverting.UninvertingReader.Type;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DoublePointField extends PointField implements DoubleValueFieldType {
+
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  public DoublePointField() {
+    super(Double.BYTES);
+  }
+
+  {
+    type = PointTypes.DOUBLE;
+  }
+
+  @Override
+  public Object toNativeType(Object val) {
+    if (val == null) return null;
+    if (val instanceof Number) return ((Number) val).doubleValue();
+    if (val instanceof String) return Double.parseDouble((String) val);
+    return super.toNativeType(val);
+  }
+
+  public Query getRangeQuery(QParser parser, SchemaField field, String min, String max, boolean minInclusive,
+      boolean maxInclusive) {
+    double actualMin, actualMax;
+    if (min == null) {
+      actualMin = Double.NEGATIVE_INFINITY;
+    } else {
+      actualMin = Double.parseDouble(min);
+      if (!minInclusive) {
+        actualMin = Math.nextUp(actualMin);
+      }
+    }
+    if (max == null) {
+      actualMax = Double.POSITIVE_INFINITY;
+    } else {
+      actualMax = Double.parseDouble(max);
+      if (!maxInclusive) {
+        actualMax = Math.nextDown(actualMax);
+      }
+    }
+    return DoublePoint.newRangeQuery(field.getName(), actualMin, actualMax);
+  }
+
+  @Override
+  protected ValueSource getSingleValueSource(SortedSetSelector.Type choice, SchemaField f) {
+    return new SortedSetFieldSource(f.getName(), choice) {
+      @Override
+      public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+        SortedSetFieldSource thisAsSortedSetFieldSource = this; // needed for nested anon class ref
+
+        SortedSetDocValues sortedSet = DocValues.getSortedSet(readerContext.reader(), field);
+        SortedDocValues view = SortedSetSelector.wrap(sortedSet, selector);
+
+        return new DoubleDocValues(thisAsSortedSetFieldSource) {
+          private int lastDocID;
+
+          private boolean setDoc(int docID) throws IOException {
+            if (docID < lastDocID) {
+              throw new IllegalArgumentException("docs out of order: lastDocID=" + lastDocID + " docID=" + docID);
+            }
+            if (docID > view.docID()) {
+              return docID == view.advance(docID);
+            } else {
+              return docID == view.docID();
+            }
+          }
+          
+          @Override
+          public double doubleVal(int doc) throws IOException {
+            if (setDoc(doc)) {
+              BytesRef bytes = view.binaryValue();
+              assert bytes.length > 0;
+              return DoublePoint.decodeDimension(bytes.bytes, bytes.offset);
+            } else {
+              return 0D;
+            }
+          }
+
+          @Override
+          public boolean exists(int doc) throws IOException {
+            return setDoc(doc);
+          }
+
+          @Override
+          public ValueFiller getValueFiller() {
+            return new ValueFiller() {
+              private final MutableValueDouble mval = new MutableValueDouble();
+              
+              @Override
+              public MutableValue getValue() {
+                return mval;
+              }
+              
+              @Override
+              public void fillValue(int doc) throws IOException {
+                if (setDoc(doc)) {
+                  BytesRef bytes = view.binaryValue();
+                  mval.exists = true;
+                  mval.value = DoublePoint.decodeDimension(bytes.bytes, bytes.offset);
+                } else {
+                  mval.exists = false;
+                  mval.value = 0;
+                }
+              }
+            };
+          }
+        };
+      }
+    };
+  }
+
+  @Override
+  public Object toObject(SchemaField sf, BytesRef term) {
+    return DoublePoint.decodeDimension(term.bytes, term.offset);
+  }
+
+  @Override
+  protected Query getExactQuery(QParser parser, SchemaField field, String externalVal) {
+    // TODO: better handling of string->int conversion
+    return DoublePoint.newExactQuery(field.getName(), Double.parseDouble(externalVal));
+  }
+
+  @Override
+  protected String indexedToReadable(BytesRef indexedForm) {
+    return Double.toString(DoublePoint.decodeDimension(indexedForm.bytes, indexedForm.offset));
+  }
+
+  @Override
+  public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
+    result.grow(Double.BYTES);
+    result.setLength(Double.BYTES);
+    DoublePoint.encodeDimension(Double.parseDouble(val.toString()), result.bytes(), 0);
+  }
+  
+  @Override
+  protected BytesRef storedToIndexedByteRef(IndexableField f) {
+    BytesRef bytes = new BytesRef(new byte[Double.BYTES], 0, Double.BYTES);
+    DoublePoint.encodeDimension(f.numericValue().doubleValue(), bytes.bytes, 0);
+    return bytes;
+  }
+  
+
+  @Override
+  public SortField getSortField(SchemaField field, boolean top) {
+    field.checkSortability();
+
+    Object missingValue = null;
+    boolean sortMissingLast = field.sortMissingLast();
+    boolean sortMissingFirst = field.sortMissingFirst();
+
+    if (sortMissingLast) {
+      missingValue = top ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+    } else if (sortMissingFirst) {
+      missingValue = top ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+    }
+    SortField sf = new SortField(field.getName(), SortField.Type.DOUBLE, top);
+    sf.setMissingValue(missingValue);
+    return sf;
+  }
+
+  @Override
+  public Type getUninversionType(SchemaField sf) {
+    if (sf.multiValued()) {
+      return Type.SORTED_SET_DOUBLE;
+    } else {
+      return Type.DOUBLE_POINT;
+    }
+  }
+
+  @Override
+  public ValueSource getValueSource(SchemaField field, QParser qparser) {
+    field.checkFieldCacheSource();
+    return new DoubleFieldSource(field.getName());
+  }
+
+  @Override
+  public LegacyNumericType getNumericType() {
+    return LegacyNumericType.DOUBLE;
+  }
+
+  @Override
+  public IndexableField createField(SchemaField field, Object value, float boost) {
+    if (!isFieldUsed(field)) return null;
+
+    if (boost != 1.0 && log.isTraceEnabled()) {
+      log.trace("Can't use document/field boost for PointField. Field: " + field.getName() + ", boost: " + boost);
+    }
+    double doubleValue = (value instanceof Number) ? ((Number) value).doubleValue() : Double.parseDouble(value.toString());
+    return new DoublePoint(field.getName(), doubleValue);
+  }
+
+  @Override
+  protected StoredField getStoredField(SchemaField sf, Object value) {
+    return new StoredField(sf.getName(), (Double) this.toNativeType(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bc904135/solr/core/src/java/org/apache/solr/schema/IntPointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/IntPointField.java b/solr/core/src/java/org/apache/solr/schema/IntPointField.java
new file mode 100644
index 0000000..4f31a79
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/IntPointField.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.schema;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Map;
+
+import org.apache.lucene.document.IntPoint;
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.legacy.LegacyNumericType;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.docvalues.IntDocValues;
+import org.apache.lucene.queries.function.valuesource.IntFieldSource;
+import org.apache.lucene.queries.function.valuesource.SortedSetFieldSource;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.SortedSetSelector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.util.mutable.MutableValue;
+import org.apache.lucene.util.mutable.MutableValueInt;
+import org.apache.solr.search.QParser;
+import org.apache.solr.uninverting.UninvertingReader.Type;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A numeric field that can contain 32-bit signed two's complement integer values.
+ *
+ * <ul>
+ * <li>Min Value Allowed: -2147483648</li>
+ * <li>Max Value Allowed: 2147483647</li>
+ * </ul>
+ * 
+ * @see Integer
+ */
+public class IntPointField extends PointField implements IntValueFieldType {
+
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  public IntPointField() {
+    super(Integer.BYTES);
+  }
+
+  {
+    type = PointTypes.INTEGER;
+  }
+
+  @Override
+  public Object toNativeType(Object val) {
+    if (val == null) return null;
+    if (val instanceof Number) return ((Number) val).intValue();
+    try {
+      if (val instanceof String) return Integer.parseInt((String) val);
+    } catch (NumberFormatException e) {
+      Float v = Float.parseFloat((String) val);
+      return v.intValue();
+    }
+    return super.toNativeType(val);
+  }
+
+  public Query getRangeQuery(QParser parser, SchemaField field, String min, String max, boolean minInclusive,
+      boolean maxInclusive) {
+    int actualMin, actualMax;
+    if (min == null) {
+      actualMin = Integer.MIN_VALUE;
+    } else {
+      actualMin = Integer.parseInt(min);
+      if (!minInclusive) {
+        actualMin++;
+      }
+    }
+    if (max == null) {
+      actualMax = Integer.MAX_VALUE;
+    } else {
+      actualMax = Integer.parseInt(max);
+      if (!maxInclusive) {
+        actualMax--;
+      }
+    }
+    return IntPoint.newRangeQuery(field.getName(), actualMin, actualMax);
+  }
+
+  @Override
+  protected ValueSource getSingleValueSource(SortedSetSelector.Type choice, SchemaField f) {
+    return new SortedSetFieldSource(f.getName(), choice) {
+      @Override
+      public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+        SortedSetFieldSource thisAsSortedSetFieldSource = this; // needed for nested anon class ref
+
+        SortedSetDocValues sortedSet = DocValues.getSortedSet(readerContext.reader(), field);
+        SortedDocValues view = SortedSetSelector.wrap(sortedSet, selector);
+
+        return new IntDocValues(thisAsSortedSetFieldSource) {
+          private int lastDocID;
+
+          private boolean setDoc(int docID) throws IOException {
+            if (docID < lastDocID) {
+              throw new IllegalArgumentException("docs out of order: lastDocID=" + lastDocID + " docID=" + docID);
+            }
+            if (docID > view.docID()) {
+              return docID == view.advance(docID);
+            } else {
+              return docID == view.docID();
+            }
+          }
+          
+          @Override
+          public int intVal(int doc) throws IOException {
+            if (setDoc(doc)) {
+              BytesRef bytes = view.binaryValue();
+              assert bytes.length > 0;
+              return IntPoint.decodeDimension(bytes.bytes, bytes.offset);
+            } else {
+              return 0;
+            }
+          }
+
+          @Override
+          public boolean exists(int doc) throws IOException {
+            return setDoc(doc);
+          }
+
+          @Override
+          public ValueFiller getValueFiller() {
+            return new ValueFiller() {
+              private final MutableValueInt mval = new MutableValueInt();
+              
+              @Override
+              public MutableValue getValue() {
+                return mval;
+              }
+              
+              @Override
+              public void fillValue(int doc) throws IOException {
+                if (setDoc(doc)) {
+                  BytesRef bytes = view.binaryValue();
+                  mval.exists = true;
+                  mval.value = IntPoint.decodeDimension(bytes.bytes, bytes.offset);
+                } else {
+                  mval.exists = false;
+                  mval.value = 0;
+                }
+              }
+            };
+          }
+        };
+      }
+    };
+  }
+
+  @Override
+  public Object toObject(SchemaField sf, BytesRef term) {
+    return IntPoint.decodeDimension(term.bytes, term.offset);
+  }
+
+  @Override
+  protected Query getExactQuery(QParser parser, SchemaField field, String externalVal) {
+    // TODO: better handling of string->int conversion
+    return IntPoint.newExactQuery(field.getName(), Integer.parseInt(externalVal));
+  }
+
+  @Override
+  protected String indexedToReadable(BytesRef indexedForm) {
+    return Integer.toString(IntPoint.decodeDimension(indexedForm.bytes, indexedForm.offset));
+  }
+
+  @Override
+  public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
+    result.grow(Integer.BYTES);
+    result.setLength(Integer.BYTES);
+    IntPoint.encodeDimension(Integer.parseInt(val.toString()), result.bytes(), 0);
+  }
+  
+  @Override
+  protected BytesRef storedToIndexedByteRef(IndexableField f) {
+    BytesRef bytes = new BytesRef(new byte[Integer.BYTES], 0, Integer.BYTES);
+    IntPoint.encodeDimension(f.numericValue().intValue(), bytes.bytes, 0);
+    return bytes;
+  }
+  
+
+  @Override
+  public SortField getSortField(SchemaField field, boolean top) {
+    field.checkSortability();
+
+    Object missingValue = null;
+    boolean sortMissingLast = field.sortMissingLast();
+    boolean sortMissingFirst = field.sortMissingFirst();
+
+    if (sortMissingLast) {
+      missingValue = top ? Integer.MIN_VALUE : Integer.MAX_VALUE;
+    } else if (sortMissingFirst) {
+      missingValue = top ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+    }
+    SortField sf = new SortField(field.getName(), SortField.Type.INT, top);
+    sf.setMissingValue(missingValue);
+    return sf;
+  }
+
+  @Override
+  public Type getUninversionType(SchemaField sf) {
+    if (sf.multiValued()) {
+      return Type.SORTED_SET_INTEGER;
+    } else {
+      return Type.INTEGER_POINT;
+    }
+  }
+
+  @Override
+  public ValueSource getValueSource(SchemaField field, QParser qparser) {
+    field.checkFieldCacheSource();
+    return new IntFieldSource(field.getName());
+  }
+
+  @Override
+  public LegacyNumericType getNumericType() {
+    return LegacyNumericType.INT;
+  }
+
+  @Override
+  public IndexableField createField(SchemaField field, Object value, float boost) {
+    if (!isFieldUsed(field)) return null;
+
+    if (boost != 1.0 && log.isTraceEnabled()) {
+      log.trace("Can't use document/field boost for PointField. Field: " + field.getName() + ", boost: " + boost);
+    }
+    int intValue = (value instanceof Number) ? ((Number) value).intValue() : Integer.parseInt(value.toString());
+    return new IntPoint(field.getName(), intValue);
+  }
+
+  @Override
+  protected StoredField getStoredField(SchemaField sf, Object value) {
+    return new StoredField(sf.getName(), (Integer) this.toNativeType(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bc904135/solr/core/src/java/org/apache/solr/schema/PointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/PointField.java b/solr/core/src/java/org/apache/solr/schema/PointField.java
new file mode 100644
index 0000000..7d4e125
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/PointField.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.schema;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.document.SortedSetDocValuesField;
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SortedSetSelector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.util.CharsRef;
+import org.apache.lucene.util.CharsRefBuilder;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.response.TextResponseWriter;
+import org.apache.solr.search.QParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides field types to support for Lucene's {@link
+ * org.apache.lucene.document.IntPoint}, {@link org.apache.lucene.document.LongPoint}, {@link org.apache.lucene.document.FloatPoint} and
+ * {@link org.apache.lucene.document.DoublePoint}.
+ * See {@link org.apache.lucene.search.PointRangeQuery} for more details.
+ * It supports integer, float, long, double and date types.
+ */
+public abstract class PointField extends PrimitiveFieldType {
+  
+  private final int typeByteLength;
+
+  public enum PointTypes {
+    INTEGER,
+    LONG,
+    FLOAT,
+    DOUBLE,
+    DATE
+  }
+  
+  protected PointTypes type;
+
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+  
+  protected PointField(int typeByteLength) {
+    this.typeByteLength = typeByteLength;
+  }
+  
+  @Override
+  public boolean isPointField() {
+    return true;
+  }
+  
+  @Override
+  public final ValueSource getSingleValueSource(MultiValueSelector choice, SchemaField field, QParser parser) {
+    // trivial base case
+    if (!field.multiValued()) {
+      // single value matches any selector
+      return getValueSource(field, parser);
+    }
+
+    // Point fields don't support UninvertingReader. See SOLR-9202
+    if (!field.hasDocValues()) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+                              "docValues='true' is required to select '" + choice.toString() +
+                              "' value from multivalued field ("+ field.getName() +") at query time");
+    }
+    
+    // multivalued Point fields all use SortedSetDocValues, so we give a clean error if that's
+    // not supported by the specified choice, else we delegate to a helper
+    SortedSetSelector.Type selectorType = choice.getSortedSetSelectorType();
+    if (null == selectorType) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+                              choice.toString() + " is not a supported option for picking a single value"
+                              + " from the multivalued field: " + field.getName() +
+                              " (type: " + this.getTypeName() + ")");
+    }
+    
+    return getSingleValueSource(selectorType, field);
+  }
+
+  /**
+   * Helper method that will only be called for multivalued Point fields that have doc values.
+   * Default impl throws an error indicating that selecting a single value from this multivalued 
+   * field is not supported for this field type
+   *
+   * @param choice the selector Type to use, will never be null
+   * @param field the field to use, garunteed to be multivalued.
+   * @see #getSingleValueSource(MultiValueSelector,SchemaField,QParser) 
+   */
+  protected abstract ValueSource getSingleValueSource(SortedSetSelector.Type choice, SchemaField field);
+
+  @Override
+  public boolean isTokenized() {
+    return false;
+  }
+
+  @Override
+  public boolean multiValuedFieldCache() {
+    return false;
+  }
+
+  /**
+   * @return the type of this field
+   */
+  public PointTypes getType() {
+    return type;
+  }
+
+//  @Override
+//  public FieldType.LegacyNumericType getNumericType() {
+//    switch (type) {
+//      case INTEGER:
+//        return FieldType.LegacyNumericType.INT;
+//      case LONG:
+//      case DATE:
+//        return FieldType.LegacyNumericType.LONG;
+//      case FLOAT:
+//        return FieldType.LegacyNumericType.FLOAT;
+//      case DOUBLE:
+//        return FieldType.LegacyNumericType.DOUBLE;
+//      default:
+//        throw new AssertionError();
+//    }
+//  }
+  
+  @Override
+  public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) {
+    if (!field.indexed() && field.hasDocValues()) {
+      // currently implemented as singleton range
+      return getRangeQuery(parser, field, externalVal, externalVal, true, true);
+    } else {
+      return getExactQuery(parser, field, externalVal);
+    }
+  }
+
+  protected abstract Query getExactQuery(QParser parser, SchemaField field, String externalVal);
+
+  @Override
+  public String storedToReadable(IndexableField f) {
+    return toExternal(f);
+  }
+
+  @Override
+  public String toInternal(String val) {
+    return toInternalByteRef(val).utf8ToString();
+  }
+  
+  public BytesRef toInternalByteRef(String val) {
+    final BytesRefBuilder bytes = new BytesRefBuilder();
+    readableToIndexed(val, bytes);
+    return bytes.get();
+  }
+  
+  @Override
+  public void write(TextResponseWriter writer, String name, IndexableField f) throws IOException {
+    writer.writeVal(name, f.numericValue());
+  }
+
+  @Override
+  public String storedToIndexed(IndexableField f) {
+    return storedToIndexedByteRef(f).utf8ToString();
+  }
+  
+  @Override
+  public CharsRef indexedToReadable(BytesRef indexedForm, CharsRefBuilder charsRef) {
+    final String value = indexedToReadable(indexedForm);
+    charsRef.grow(value.length());
+    charsRef.setLength(value.length());
+    value.getChars(0, charsRef.length(), charsRef.chars(), 0);
+    return charsRef.get();
+  }
+  
+  @Override
+  public String indexedToReadable(String indexedForm) {
+    return indexedToReadable(new BytesRef(indexedForm));
+  }
+  
+  protected abstract String indexedToReadable(BytesRef indexedForm);
+  
+  protected abstract BytesRef storedToIndexedByteRef(IndexableField f);
+
+  protected boolean isFieldUsed(SchemaField field) {
+    boolean indexed = field.indexed();
+    boolean stored = field.stored();
+    boolean docValues = field.hasDocValues();
+
+    if (!indexed && !stored && !docValues) {
+      if (log.isTraceEnabled()) {
+        log.trace("Ignoring unindexed/unstored field: " + field);
+      }
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  public List<IndexableField> createFields(SchemaField sf, Object value, float boost) {
+    if (!(sf.hasDocValues() || sf.stored())) {
+      return Collections.singletonList(createField(sf, value, boost));
+    }
+    List<IndexableField> fields = new ArrayList<>();
+    final IndexableField field = createField(sf, value, boost);
+    fields.add(field);
+    
+    if (sf.hasDocValues()) {
+      if (sf.multiValued()) {
+        fields.add(new SortedSetDocValuesField(sf.getName(), storedToIndexedByteRef(field)));
+      } else {
+        final long bits;
+        if (field.numericValue() instanceof Integer || field.numericValue() instanceof Long) {
+          bits = field.numericValue().longValue();
+        } else if (field.numericValue() instanceof Float) {
+          bits = Float.floatToIntBits(field.numericValue().floatValue());
+        } else {
+          assert field.numericValue() instanceof Double;
+          bits = Double.doubleToLongBits(field.numericValue().doubleValue());
+        }
+        fields.add(new NumericDocValuesField(sf.getName(), bits));
+      }
+    }
+    if (sf.stored()) {
+      fields.add(getStoredField(sf, value));
+    }
+    return fields;
+  }
+
+  protected abstract StoredField getStoredField(SchemaField sf, Object value);
+
+  @Override
+  public void checkSchemaField(final SchemaField field) {
+    // PointFields support DocValues
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bc904135/solr/core/src/test-files/solr/collection1/conf/schema-point.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema-point.xml b/solr/core/src/test-files/solr/collection1/conf/schema-point.xml
new file mode 100644
index 0000000..6a5ccd6
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/schema-point.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ This is the Solr schema file. This file should be named "schema.xml" and
+ should be in the conf directory under the solr home
+ (i.e. ./solr/conf/schema.xml by default)
+ or located where the classloader for the Solr webapp can find it.
+
+ This example schema is the recommended starting point for users.
+ It should be kept correct and concise, usable out-of-the-box.
+
+ For more information, on how to customize this file, please see
+ http://wiki.apache.org/solr/SchemaXml
+-->
+
+<schema name="example" version="1.2">
+  <!-- attribute "name" is the name of this schema and is only used for display purposes.
+       Applications should change this to reflect the nature of the search collection.
+       version="1.1" is Solr's version number for the schema syntax and semantics.  It should
+       not normally be changed by applications.
+       1.0: multiValued attribute did not exist, all fields are multiValued by nature
+       1.1: multiValued attribute introduced, false by default -->
+
+  <types>
+    <!-- field type definitions. The "name" attribute is
+       just a label to be used by field definitions.  The "class"
+       attribute and any other attributes determine the real
+       behavior of the fieldType.
+         Class names starting with "solr" refer to java classes in the
+       org.apache.solr.analysis package.
+    -->
+
+    <!-- The StrField type is not analyzed, but indexed/stored verbatim.
+       - StrField and TextField support an optional compressThreshold which
+       limits compression (if enabled in the derived fields) to values which
+       exceed a certain size (in characters).
+    -->
+    <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
+
+    <!-- boolean type: "true" or "false" -->
+    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
+
+    <fieldType name="pint" class="solr.IntPointField"/>
+    <fieldType name="pdouble" class="solr.DoublePointField"/>
+    <fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="date" class="solr.TrieDateField" sortMissingLast="true" omitNorms="true"/>
+
+
+    <!-- The "RandomSortField" is not used to store or search any
+         data.  You can declare fields of this type it in your schema
+         to generate psuedo-random orderings of your docs for sorting
+         purposes.  The ordering is generated based on the field name
+         and the version of the index, As long as the index version
+         remains unchanged, and the same field name is reused,
+         the ordering of the docs will be consistent.
+         If you want differend psuedo-random orderings of documents,
+         for the same version of the index, use a dynamicField and
+         change the name
+     -->
+    <fieldType name="random" class="solr.RandomSortField" indexed="true" />
+ </types>
+
+
+ <fields>
+   <field name="id" type="string"/>
+   <field name="text" type="string"/>
+   <field name="_version_" type="long" indexed="true" stored="true" multiValued="false" />
+   <field name="signatureField" type="string" indexed="true" stored="false"/>
+   <dynamicField name="*_s"  type="string"  indexed="true"  stored="true"/>
+   <dynamicField name="*_sS" type="string"  indexed="false" stored="true"/>
+   <dynamicField name="*_i"  type="int"    indexed="true"  stored="true"/>
+   <dynamicField name="*_l"  type="long"   indexed="true"  stored="true"/>
+   <dynamicField name="*_f"  type="float"  indexed="true"  stored="true"/>
+   <dynamicField name="*_d"  type="double" indexed="true"  stored="true"/>
+   <dynamicField name="*_dt"  type="date" indexed="true"  stored="true"/>
+   
+   <dynamicField name="*_p_i"  type="pint"    indexed="true"  stored="true"/>
+   <dynamicField name="*_p_i_dv"  type="pint"    indexed="true"  stored="true" docValues="true"/>
+   <dynamicField name="*_p_i_mv"  type="pint"    indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="*_p_i_mv_dv"  type="pint"    indexed="true"  stored="true" docValues="true" multiValued="true"/>
+   <dynamicField name="*_p_i_ni_dv"  type="pint"    indexed="false"  stored="true" docValues="true"/>
+   <dynamicField name="*_p_i_ni_mv_dv"  type="pint"    indexed="false"  stored="true" docValues="true" multiValued="true"/>
+   
+   <dynamicField name="*_p_d"  type="pdouble"    indexed="true"  stored="true"/>
+   <dynamicField name="*_p_d_dv"  type="pdouble"    indexed="true"  stored="true" docValues="true"/>
+   <dynamicField name="*_p_d_mv"  type="pdouble"    indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="*_p_d_mv_dv"  type="pdouble"    indexed="true"  stored="true" docValues="true" multiValued="true"/>
+   <dynamicField name="*_p_d_ni_dv"  type="pdouble"    indexed="false"  stored="true" docValues="true"/>
+   <dynamicField name="*_p_d_ni_mv_dv"  type="pdouble"    indexed="false"  stored="true" docValues="true" multiValued="true"/>
+   
+   <!--  dynamicField name="*_l"  type="plong"   indexed="true"  stored="true"/>
+   <dynamicField name="*_f"  type="pfloat"  indexed="true"  stored="true"/>
+   <dynamicField name="*_d"  type="pdouble" indexed="true"  stored="true"/>
+   <dynamicField name="*_dt"  type="pdate" indexed="true"  stored="true"/ -->
+
+ </fields>
+
+ <!-- Field to use to determine and enforce document uniqueness.
+      Unless this field is marked with required="false", it will be a required field
+   -->
+ <uniqueKey>id</uniqueKey>
+
+
+</schema>