org.apache.solr.schema.TestPointFields.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.schema.TestPointFields.java

Source

/*
 * 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.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.DoublePoint;
import org.apache.lucene.document.FloatPoint;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.document.SortedNumericDocValuesField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.PointValues;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexOrDocValuesQuery;
import org.apache.lucene.search.PointRangeQuery;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.index.SlowCompositeReaderWrapper;
import org.apache.solr.schema.IndexSchema.DynamicField;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.search.SolrQueryParser;
import org.apache.solr.util.DateMathParser;
import org.apache.solr.util.RefCounted;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

/**
 * Tests for PointField functionality
 *
 *
 */
public class TestPointFields extends SolrTestCaseJ4 {

    @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", false);
        doTestIntPointFieldExactQuery("number_p_i_mv", false);
        doTestIntPointFieldExactQuery("number_p_i_dv", false);
        doTestIntPointFieldExactQuery("number_p_i_mv_dv", false);
        doTestIntPointFieldExactQuery("number_p_i_ni_dv", false);
        doTestIntPointFieldExactQuery("number_p_i_ni_ns_dv", false);
        doTestIntPointFieldExactQuery("number_p_i_ni_mv_dv", false);
    }

    @Test
    public void testIntPointFieldNonSearchableExactQuery() throws Exception {
        doTestIntPointFieldExactQuery("number_p_i_ni", false, false);
        doTestIntPointFieldExactQuery("number_p_i_ni_ns", false, false);
    }

    @Test
    public void testIntPointFieldReturn() throws Exception {
        testPointFieldReturn("number_p_i", "int",
                new String[] { "0", "-1", "2", "3", "43", "52", "-60", "74", "80", "99" });
        testPointFieldReturn("number_p_i_dv_ns", "int",
                new String[] { "0", "-1", "2", "3", "43", "52", "-60", "74", "80", "99" });
        testPointFieldReturn("number_p_i_ni", "int",
                new String[] { "0", "-1", "2", "3", "43", "52", "-60", "74", "80", "99" });
    }

    @Test
    public void testIntPointFieldRangeQuery() throws Exception {
        doTestIntPointFieldRangeQuery("number_p_i", "int", false);
        doTestIntPointFieldRangeQuery("number_p_i_ni_ns_dv", "int", false);
        doTestIntPointFieldRangeQuery("number_p_i_dv", "int", false);
    }

    @Test
    public void testIntPointFieldNonSearchableRangeQuery() throws Exception {
        doTestPointFieldNonSearchableRangeQuery("number_p_i_ni", "42");
        doTestPointFieldNonSearchableRangeQuery("number_p_i_ni_ns", "42");
        doTestPointFieldNonSearchableRangeQuery("number_p_i_ni_ns_mv", "42", "666");
    }

    @Test
    public void testIntPointFieldSortAndFunction() throws Exception {

        final SortedSet<String> regexToTest = dynFieldRegexesForType(IntPointField.class);
        final String[] sequential = new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };

        for (String r : Arrays.asList("*_p_i", "*_p_i_dv", "*_p_i_dv_ns", "*_p_i_ni_dv", "*_p_i_ni_dv_ns",
                "*_p_i_ni_ns_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSort(r.replace("*", "number"), sequential);
            // TODO: test some randomly generated (then sorted) arrays (with dups and/or missing values)

            doTestIntPointFunctionQuery(r.replace("*", "number"), "int");
        }

        for (String r : Arrays.asList("*_p_i_ni", "*_p_i_ni_ns")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "w/o docValues", "42");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "w/o docValues", "42");
        }

        for (String r : Arrays.asList("*_p_i_mv", "*_p_i_ni_mv", "*_p_i_ni_mv_dv", "*_p_i_ni_dv_ns_mv",
                "*_p_i_ni_ns_mv", "*_p_i_dv_ns_mv", "*_p_i_mv_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "42");
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "42", "666");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "42");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "42", "666");
        }

        assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);
    }

    @Test
    public void testIntPointFieldFacetField() throws Exception {
        testPointFieldFacetField("number_p_i", "number_p_i_dv", getSequentialStringArrayWithInts(10));
    }

    @Test
    public void testIntPointFieldRangeFacet() throws Exception {
        doTestIntPointFieldRangeFacet("number_p_i_dv", "number_p_i");
    }

    @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" }, 0D, 9D, "10", "1", 0D);
        testPointStats("number_p_i", "number_p_i_mv_dv",
                new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, 0D, 9D, "10", "1", 0D);
    }

    @Test
    public void testIntPointFieldMultiValuedExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_i_mv", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedExactQuery("number_p_i_ni_mv_dv", getSequentialStringArrayWithInts(20));
    }

    @Test
    public void testIntPointFieldMultiValuedNonSearchableExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_i_ni_mv", getSequentialStringArrayWithInts(20), false);
        testPointFieldMultiValuedExactQuery("number_p_i_ni_ns_mv", getSequentialStringArrayWithInts(20), false);
    }

    @Test
    public void testIntPointFieldMultiValuedReturn() throws Exception {
        testPointFieldMultiValuedReturn("number_p_i_mv", "int", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedReturn("number_p_i_ni_mv_dv", "int", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedReturn("number_p_i_dv_ns_mv", "int", getSequentialStringArrayWithInts(20));
    }

    @Test
    public void testIntPointFieldMultiValuedRangeQuery() throws Exception {
        testPointFieldMultiValuedRangeQuery("number_p_i_mv", "int", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedRangeQuery("number_p_i_ni_mv_dv", "int", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedRangeQuery("number_p_i_mv_dv", "int", getSequentialStringArrayWithInts(20));
    }

    @Test
    public void testIntPointFieldNotIndexed() throws Exception {
        doTestFieldNotIndexed("number_p_i_ni", getSequentialStringArrayWithInts(10));
        doTestFieldNotIndexed("number_p_i_ni_mv", getSequentialStringArrayWithInts(10));
    }

    //TODO MV SORT?
    @Test
    public void testIntPointFieldMultiValuedFacetField() throws Exception {
        testPointFieldMultiValuedFacetField("number_p_i_mv", "number_p_i_mv_dv",
                getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedFacetField("number_p_i_mv", "number_p_i_mv_dv",
                getRandomStringArrayWithInts(20, false));
    }

    @Test
    public void testIntPointFieldMultiValuedRangeFacet() throws Exception {
        doTestIntPointFieldMultiValuedRangeFacet("number_p_i_mv_dv", "number_p_i_mv");
    }

    @Test
    public void testIntPointMultiValuedFunctionQuery() throws Exception {
        testPointMultiValuedFunctionQuery("number_p_i_mv", "number_p_i_mv_dv", "int",
                getSequentialStringArrayWithInts(20));
    }

    @Test
    public void testIntPointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        testIntPointFieldsAtomicUpdates("number_p_i", "int");
        testIntPointFieldsAtomicUpdates("number_p_i_dv", "int");
        testIntPointFieldsAtomicUpdates("number_p_i_dv_ns", "int");
    }

    @Test
    public void testMultiValuedIntPointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        testMultiValuedIntPointFieldsAtomicUpdates("number_p_i_mv", "int");
        testMultiValuedIntPointFieldsAtomicUpdates("number_p_i_ni_mv_dv", "int");
        testMultiValuedIntPointFieldsAtomicUpdates("number_p_i_dv_ns_mv", "int");
    }

    @Test
    public void testIntPointSetQuery() throws Exception {
        doTestSetQueries("number_p_i", getRandomStringArrayWithInts(20, false), false);
        doTestSetQueries("number_p_i_mv", getRandomStringArrayWithInts(20, false), true);
        doTestSetQueries("number_p_i_ni_dv", getRandomStringArrayWithInts(20, false), false);
    }

    // DoublePointField

    @Test
    public void testDoublePointFieldExactQuery() throws Exception {
        doTestFloatPointFieldExactQuery("number_d");
        doTestFloatPointFieldExactQuery("number_p_d");
        doTestFloatPointFieldExactQuery("number_p_d_mv");
        doTestFloatPointFieldExactQuery("number_p_d_dv");
        doTestFloatPointFieldExactQuery("number_p_d_mv_dv");
        doTestFloatPointFieldExactQuery("number_p_d_ni_dv");
        doTestFloatPointFieldExactQuery("number_p_d_ni_ns_dv");
        doTestFloatPointFieldExactQuery("number_p_d_ni_dv_ns");
        doTestFloatPointFieldExactQuery("number_p_d_ni_mv_dv");
    }

    @Test
    public void testDoublePointFieldNonSearchableExactQuery() throws Exception {
        doTestFloatPointFieldExactQuery("number_p_d_ni", false);
        doTestFloatPointFieldExactQuery("number_p_d_ni_ns", false);
    }

    @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" });
        testPointFieldReturn("number_p_d_dv_ns", "double",
                new String[] { "0.0", "1.2", "2.5", "3.02", "0.43", "5.2", "6.01", "74.0", "80.0", "9.9" });
        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 {
        doTestFloatPointFieldRangeQuery("number_p_d", "double", true);
        doTestFloatPointFieldRangeQuery("number_p_d_ni_ns_dv", "double", true);
        doTestFloatPointFieldRangeQuery("number_p_d_dv", "double", true);
    }

    @Test
    public void testDoubleFieldNonSearchableRangeQuery() throws Exception {
        doTestPointFieldNonSearchableRangeQuery("number_p_d_ni", "42.3");
        doTestPointFieldNonSearchableRangeQuery("number_p_d_ni_ns", "42.3");
        doTestPointFieldNonSearchableRangeQuery("number_p_d_ni_ns_mv", "42.3", "-66.6");
    }

    @Test
    public void testDoublePointFieldSortAndFunction() throws Exception {
        final SortedSet<String> regexToTest = dynFieldRegexesForType(DoublePointField.class);
        final String[] sequential = new String[] { "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0",
                "9.0" };
        final String[] randstrs = getRandomStringArrayWithDoubles(10, true);

        for (String r : Arrays.asList("*_p_d", "*_p_d_dv", "*_p_d_dv_ns", "*_p_d_ni_dv", "*_p_d_ni_dv_ns",
                "*_p_d_ni_ns_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSort(r.replace("*", "number"), sequential);
            doTestPointFieldSort(r.replace("*", "number"), randstrs);
            // TODO: test some randomly generated (then sorted) arrays (with dups and/or missing values)

            doTestFloatPointFunctionQuery(r.replace("*", "number"), "double");
        }

        for (String r : Arrays.asList("*_p_d_ni", "*_p_d_ni_ns")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "w/o docValues", "42.34");

            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "w/o docValues", "42.34");
        }

        for (String r : Arrays.asList("*_p_d_mv", "*_p_d_ni_mv", "*_p_d_ni_mv_dv", "*_p_d_ni_dv_ns_mv",
                "*_p_d_ni_ns_mv", "*_p_d_dv_ns_mv", "*_p_d_mv_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "42.34");
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "42.34", "66.6");

            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "42.34");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "42.34", "66.6");
        }

        assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);

    }

    @Test
    public void testDoublePointFieldFacetField() throws Exception {
        testPointFieldFacetField("number_p_d", "number_p_d_dv", getSequentialStringArrayWithDoubles(10));
        clearIndex();
        assertU(commit());
        testPointFieldFacetField("number_p_d", "number_p_d_dv", getRandomStringArrayWithDoubles(10, false));
    }

    @Test
    public void testDoublePointFieldRangeFacet() throws Exception {
        doTestFloatPointFieldRangeFacet("number_p_d_dv", "number_p_d");
    }

    @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.0D,
                9.9D, "10", "1", 1E-10D);
        testPointStats("number_p_d_mv", "number_p_d_mv_dv",
                new String[] { "-10.0", "1.1", "2.2", "3.3", "4.4", "5.5", "6.6", "7.7", "8.8", "9.9" }, -10.0D,
                9.9D, "10", "1", 1E-10D);
    }

    @Test
    public void testDoublePointFieldMultiValuedExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_d_mv", getRandomStringArrayWithDoubles(20, false));
        testPointFieldMultiValuedExactQuery("number_p_d_ni_mv_dv", getRandomStringArrayWithDoubles(20, false));
    }

    @Test
    public void testDoublePointFieldMultiValuedNonSearchableExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_d_ni_mv", getRandomStringArrayWithDoubles(20, false), false);
        testPointFieldMultiValuedExactQuery("number_p_d_ni_ns_mv", getRandomStringArrayWithDoubles(20, false),
                false);
    }

    @Test
    public void testDoublePointFieldMultiValuedReturn() throws Exception {
        testPointFieldMultiValuedReturn("number_p_d_mv", "double", getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedReturn("number_p_d_ni_mv_dv", "double", getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedReturn("number_p_d_dv_ns_mv", "double", getSequentialStringArrayWithDoubles(20));
    }

    @Test
    public void testDoublePointFieldMultiValuedRangeQuery() throws Exception {
        testPointFieldMultiValuedRangeQuery("number_p_d_mv", "double", getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedRangeQuery("number_p_d_ni_mv_dv", "double",
                getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedRangeQuery("number_p_d_mv_dv", "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",
                getRandomStringArrayWithDoubles(20, false));
    }

    @Test
    public void testDoublePointFieldMultiValuedRangeFacet() throws Exception {
        doTestDoublePointFieldMultiValuedRangeFacet("number_p_d_mv_dv", "number_p_d_mv");
    }

    @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));
    }

    @Test
    public void testDoublePointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        doTestFloatPointFieldsAtomicUpdates("number_p_d", "double");
        doTestFloatPointFieldsAtomicUpdates("number_p_d_dv", "double");
        doTestFloatPointFieldsAtomicUpdates("number_p_d_dv_ns", "double");
    }

    @Test
    public void testMultiValuedDoublePointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        testMultiValuedFloatPointFieldsAtomicUpdates("number_p_d_mv", "double");
        testMultiValuedFloatPointFieldsAtomicUpdates("number_p_d_ni_mv_dv", "double");
        testMultiValuedFloatPointFieldsAtomicUpdates("number_p_d_dv_ns_mv", "double");
    }

    @Test
    public void testDoublePointFieldNotIndexed() throws Exception {
        doTestFieldNotIndexed("number_p_d_ni", getSequentialStringArrayWithDoubles(10));
        doTestFieldNotIndexed("number_p_d_ni_mv", getSequentialStringArrayWithDoubles(10));
    }

    private void doTestFloatPointFieldsAtomicUpdates(String field, String type) throws Exception {
        assertU(adoc(sdoc("id", "1", field, "1.1234")));
        assertU(commit());

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", 1.1F))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.='2.2234']");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", -1.1F))));
        assertU(commit());

        // TODO: can this test be better?
        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.>'1.1233']",
                "//result/doc[1]/" + type + "[@name='" + field + "'][.<'1.1235']");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", 3.123F))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.='3.123']");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", 3.14F))));
        assertU(commit());
        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", 1F))));
        assertU(commit());
        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.>'4.13']",
                "//result/doc[1]/" + type + "[@name='" + field + "'][.<'4.15']");
    }

    @Test
    public void testDoublePointSetQuery() throws Exception {
        doTestSetQueries("number_p_d", getRandomStringArrayWithDoubles(20, false), false);
        doTestSetQueries("number_p_d_mv", getRandomStringArrayWithDoubles(20, false), true);
        doTestSetQueries("number_p_d_ni_dv", getRandomStringArrayWithDoubles(20, false), false);
    }

    // Float

    @Test
    public void testFloatPointFieldExactQuery() throws Exception {
        doTestFloatPointFieldExactQuery("number_p_f");
        doTestFloatPointFieldExactQuery("number_p_f_mv");
        doTestFloatPointFieldExactQuery("number_p_f_dv");
        doTestFloatPointFieldExactQuery("number_p_f_mv_dv");
        doTestFloatPointFieldExactQuery("number_p_f_ni_dv");
        doTestFloatPointFieldExactQuery("number_p_f_ni_ns_dv");
        doTestFloatPointFieldExactQuery("number_p_f_ni_dv_ns");
        doTestFloatPointFieldExactQuery("number_p_f_ni_mv_dv");
    }

    @Test
    public void testFloatPointFieldNonSearchableExactQuery() throws Exception {
        doTestFloatPointFieldExactQuery("number_p_f_ni", false);
        doTestFloatPointFieldExactQuery("number_p_f_ni_ns", false);
    }

    @Test
    public void testFloatPointFieldReturn() throws Exception {
        testPointFieldReturn("number_p_f", "float",
                new String[] { "0.0", "-1.2", "2.5", "3.02", "0.43", "5.2", "6.01", "74.0", "80.0", "9.9" });
        testPointFieldReturn("number_p_f_dv_ns", "float",
                new String[] { "0.0", "-1.2", "2.5", "3.02", "0.43", "5.2", "6.01", "74.0", "80.0", "9.9" });
        String[] arr = new String[atLeast(10)];
        for (int i = 0; i < arr.length; i++) {
            float rand = random().nextFloat() * 10;
            arr[i] = String.valueOf(rand);
        }
        testPointFieldReturn("number_p_f", "float", arr);
    }

    @Test
    public void testFloatPointFieldRangeQuery() throws Exception {
        doTestFloatPointFieldRangeQuery("number_p_f", "float", false);
        doTestFloatPointFieldRangeQuery("number_p_f_ni_ns_dv", "float", false);
        doTestFloatPointFieldRangeQuery("number_p_f_dv", "float", false);
    }

    @Test
    public void testFloatPointFieldNonSearchableRangeQuery() throws Exception {
        doTestPointFieldNonSearchableRangeQuery("number_p_f_ni", "42.3");
        doTestPointFieldNonSearchableRangeQuery("number_p_f_ni_ns", "42.3");
        doTestPointFieldNonSearchableRangeQuery("number_p_f_ni_ns_mv", "42.3", "-66.6");
    }

    @Test
    public void testFloatPointFieldSortAndFunction() throws Exception {
        final SortedSet<String> regexToTest = dynFieldRegexesForType(FloatPointField.class);
        final String[] sequential = new String[] { "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0",
                "9.0" };
        final String[] randstrs = getRandomStringArrayWithFloats(10, true);

        for (String r : Arrays.asList("*_p_f", "*_p_f_dv", "*_p_f_dv_ns", "*_p_f_ni_dv", "*_p_f_ni_dv_ns",
                "*_p_f_ni_ns_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSort(r.replace("*", "number"), sequential);
            doTestPointFieldSort(r.replace("*", "number"), randstrs);
            // TODO: test some randomly generated (then sorted) arrays (with dups and/or missing values)

            doTestFloatPointFunctionQuery(r.replace("*", "number"), "float");
        }

        for (String r : Arrays.asList("*_p_f_ni", "*_p_f_ni_ns")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "w/o docValues", "42.34");

            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "w/o docValues", "42.34");
        }

        for (String r : Arrays.asList("*_p_f_mv", "*_p_f_ni_mv", "*_p_f_ni_mv_dv", "*_p_f_ni_dv_ns_mv",
                "*_p_f_ni_ns_mv", "*_p_f_dv_ns_mv", "*_p_f_mv_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "42.34");
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "42.34", "66.6");

            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "42.34");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "42.34", "66.6");
        }

        assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);

    }

    @Test
    public void testFloatPointFieldFacetField() throws Exception {
        testPointFieldFacetField("number_p_f", "number_p_f_dv", getSequentialStringArrayWithDoubles(10));
        clearIndex();
        assertU(commit());
        testPointFieldFacetField("number_p_f", "number_p_f_dv", getRandomStringArrayWithFloats(10, false));
    }

    @Test
    public void testFloatPointFieldRangeFacet() throws Exception {
        doTestFloatPointFieldRangeFacet("number_p_f_dv", "number_p_f");
    }

    @Test
    public void testFloatPointStats() throws Exception {
        testPointStats("number_p_f", "number_p_f_dv",
                new String[] { "-10.0", "1.1", "2.2", "3.3", "4.4", "5.5", "6.6", "7.7", "8.8", "9.9" }, -10D, 9.9D,
                "10", "1", 1E-6D);
        testPointStats("number_p_f_mv", "number_p_f_mv_dv",
                new String[] { "-10.0", "1.1", "2.2", "3.3", "4.4", "5.5", "6.6", "7.7", "8.8", "9.9" }, -10D, 9.9D,
                "10", "1", 1E-6D);
    }

    @Test
    public void testFloatPointFieldMultiValuedExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_f_mv", getRandomStringArrayWithFloats(20, false));
        testPointFieldMultiValuedExactQuery("number_p_f_ni_mv_dv", getRandomStringArrayWithFloats(20, false));
    }

    @Test
    public void testFloatPointFieldMultiValuedNonSearchableExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_f_ni_mv", getRandomStringArrayWithFloats(20, false), false);
        testPointFieldMultiValuedExactQuery("number_p_f_ni_ns_mv", getRandomStringArrayWithFloats(20, false),
                false);
    }

    @Test
    public void testFloatPointFieldMultiValuedReturn() throws Exception {
        testPointFieldMultiValuedReturn("number_p_f_mv", "float", getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedReturn("number_p_f_ni_mv_dv", "float", getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedReturn("number_p_f_dv_ns_mv", "float", getSequentialStringArrayWithDoubles(20));
    }

    @Test
    public void testFloatPointFieldMultiValuedRangeQuery() throws Exception {
        testPointFieldMultiValuedRangeQuery("number_p_f_mv", "float", getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedRangeQuery("number_p_f_ni_mv_dv", "float",
                getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedRangeQuery("number_p_f_mv_dv", "float", getSequentialStringArrayWithDoubles(20));
    }

    @Test
    public void testFloatPointFieldMultiValuedRangeFacet() throws Exception {
        doTestDoublePointFieldMultiValuedRangeFacet("number_p_f_mv_dv", "number_p_f_mv");
    }

    @Test
    public void testFloatPointFieldMultiValuedFacetField() throws Exception {
        testPointFieldMultiValuedFacetField("number_p_f_mv", "number_p_f_mv_dv",
                getSequentialStringArrayWithDoubles(20));
        testPointFieldMultiValuedFacetField("number_p_f_mv", "number_p_f_mv_dv",
                getRandomStringArrayWithFloats(20, false));
    }

    @Test
    public void testFloatPointMultiValuedFunctionQuery() throws Exception {
        testPointMultiValuedFunctionQuery("number_p_f_mv", "number_p_f_mv_dv", "float",
                getSequentialStringArrayWithDoubles(20));
        testPointMultiValuedFunctionQuery("number_p_f_mv", "number_p_f_mv_dv", "float",
                getRandomStringArrayWithFloats(20, true));
    }

    @Test
    public void testFloatPointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        doTestFloatPointFieldsAtomicUpdates("number_p_f", "float");
        doTestFloatPointFieldsAtomicUpdates("number_p_f_dv", "float");
        doTestFloatPointFieldsAtomicUpdates("number_p_f_dv_ns", "float");
    }

    @Test
    public void testMultiValuedFloatePointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        testMultiValuedFloatPointFieldsAtomicUpdates("number_p_f_mv", "float");
        testMultiValuedFloatPointFieldsAtomicUpdates("number_p_f_ni_mv_dv", "float");
        testMultiValuedFloatPointFieldsAtomicUpdates("number_p_f_dv_ns_mv", "float");
    }

    @Test
    public void testFloatPointSetQuery() throws Exception {
        doTestSetQueries("number_p_f", getRandomStringArrayWithFloats(20, false), false);
        doTestSetQueries("number_p_f_mv", getRandomStringArrayWithFloats(20, false), true);
        doTestSetQueries("number_p_f_ni_dv", getRandomStringArrayWithFloats(20, false), false);
    }

    @Test
    public void testFloatPointFieldNotIndexed() throws Exception {
        doTestFieldNotIndexed("number_p_f_ni", getSequentialStringArrayWithDoubles(10));
        doTestFieldNotIndexed("number_p_f_ni_mv", getSequentialStringArrayWithDoubles(10));
    }

    // Long

    @Test
    public void testLongPointFieldExactQuery() throws Exception {
        doTestIntPointFieldExactQuery("number_p_l", true);
        doTestIntPointFieldExactQuery("number_p_l_mv", true);
        doTestIntPointFieldExactQuery("number_p_l_dv", true);
        doTestIntPointFieldExactQuery("number_p_l_mv_dv", true);
        doTestIntPointFieldExactQuery("number_p_l_ni_dv", true);
        doTestIntPointFieldExactQuery("number_p_l_ni_ns_dv", true);
        doTestIntPointFieldExactQuery("number_p_l_ni_dv_ns", true);
        doTestIntPointFieldExactQuery("number_p_l_ni_mv_dv", true);
    }

    @Test
    public void testLongPointFieldNonSearchableExactQuery() throws Exception {
        doTestIntPointFieldExactQuery("number_p_l_ni", true, false);
        doTestIntPointFieldExactQuery("number_p_l_ni_ns", true, false);
    }

    @Test
    public void testLongPointFieldReturn() throws Exception {
        testPointFieldReturn("number_p_l", "long", new String[] { "0", "-1", "2", "3", "43", "52", "-60", "74",
                "80", "99", String.valueOf(Long.MAX_VALUE) });
        testPointFieldReturn("number_p_l_dv_ns", "long", new String[] { "0", "-1", "2", "3", "43", "52", "-60",
                "74", "80", "99", String.valueOf(Long.MAX_VALUE) });
    }

    @Test
    public void testLongPointFieldRangeQuery() throws Exception {
        doTestIntPointFieldRangeQuery("number_p_l", "long", true);
        doTestIntPointFieldRangeQuery("number_p_l_ni_ns_dv", "long", true);
        doTestIntPointFieldRangeQuery("number_p_l_dv", "long", true);
    }

    @Test
    public void testLongPointFieldNonSearchableRangeQuery() throws Exception {
        doTestPointFieldNonSearchableRangeQuery("number_p_l_ni", "3333333333");
        doTestPointFieldNonSearchableRangeQuery("number_p_l_ni_ns", "3333333333");
        doTestPointFieldNonSearchableRangeQuery("number_p_l_ni_ns_mv", "3333333333", "-4444444444");
    }

    @Test
    public void testLongPointFieldSortAndFunction() throws Exception {
        final SortedSet<String> regexToTest = dynFieldRegexesForType(LongPointField.class);
        final String[] vals = new String[] { String.valueOf(Integer.MIN_VALUE), "1", "2", "3", "4", "5", "6", "7",
                String.valueOf(Integer.MAX_VALUE), String.valueOf(Long.MAX_VALUE) };

        for (String r : Arrays.asList("*_p_l", "*_p_l_dv", "*_p_l_dv_ns", "*_p_l_ni_dv", "*_p_l_ni_dv_ns",
                "*_p_l_ni_ns_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSort(r.replace("*", "number"), vals);
            // TODO: test some randomly generated (then sorted) arrays (with dups and/or missing values)

            doTestIntPointFunctionQuery(r.replace("*", "number"), "long");
        }

        for (String r : Arrays.asList("*_p_l_ni", "*_p_l_ni_ns")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "w/o docValues", "4234");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "w/o docValues", "4234");
        }

        for (String r : Arrays.asList("*_p_l_mv", "*_p_l_ni_mv", "*_p_l_ni_mv_dv", "*_p_l_ni_dv_ns_mv",
                "*_p_l_ni_ns_mv", "*_p_l_dv_ns_mv", "*_p_l_mv_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "4234");
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "4234", "66666666");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "4234");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "4234", "66666666");
        }

        assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);

    }

    @Test
    public void testLongPointFieldFacetField() throws Exception {
        testPointFieldFacetField("number_p_l", "number_p_l_dv", getSequentialStringArrayWithInts(10));
        clearIndex();
        assertU(commit());
        testPointFieldFacetField("number_p_l", "number_p_l_dv", getRandomStringArrayWithLongs(10, true));
    }

    @Test
    public void testLongPointFieldRangeFacet() throws Exception {
        doTestIntPointFieldRangeFacet("number_p_l_dv", "number_p_l");
    }

    @Test
    public void testLongPointStats() throws Exception {
        testPointStats("number_p_l", "number_p_l_dv",
                new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, 0D, 9D, "10", "1", 0D);
        testPointStats("number_p_l_mv", "number_p_l_mv_dv",
                new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, 0D, 9D, "10", "1", 0D);
    }

    @Test
    public void testLongPointFieldMultiValuedExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_l_mv", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedExactQuery("number_p_l_ni_mv_dv", getSequentialStringArrayWithInts(20));
    }

    @Test
    public void testLongPointFieldMultiValuedNonSearchableExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_l_ni_mv", getSequentialStringArrayWithInts(20), false);
        testPointFieldMultiValuedExactQuery("number_p_l_ni_ns_mv", getSequentialStringArrayWithInts(20), false);
    }

    @Test
    public void testLongPointFieldMultiValuedReturn() throws Exception {
        testPointFieldMultiValuedReturn("number_p_l_mv", "long", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedReturn("number_p_l_ni_mv_dv", "long", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedReturn("number_p_l_dv_ns_mv", "long", getSequentialStringArrayWithInts(20));
    }

    @Test
    public void testLongPointFieldMultiValuedRangeQuery() throws Exception {
        testPointFieldMultiValuedRangeQuery("number_p_l_mv", "long", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedRangeQuery("number_p_l_ni_mv_dv", "long", getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedRangeQuery("number_p_l_mv_dv", "long", getSequentialStringArrayWithInts(20));
    }

    @Test
    public void testLongPointFieldMultiValuedFacetField() throws Exception {
        testPointFieldMultiValuedFacetField("number_p_l_mv", "number_p_l_mv_dv",
                getSequentialStringArrayWithInts(20));
        testPointFieldMultiValuedFacetField("number_p_l_mv", "number_p_l_mv_dv",
                getRandomStringArrayWithLongs(20, false));
    }

    @Test
    public void testLongPointFieldMultiValuedRangeFacet() throws Exception {
        doTestIntPointFieldMultiValuedRangeFacet("number_p_l_mv_dv", "number_p_l_mv");
    }

    @Test
    public void testLongPointMultiValuedFunctionQuery() throws Exception {
        testPointMultiValuedFunctionQuery("number_p_l_mv", "number_p_l_mv_dv", "long",
                getSequentialStringArrayWithInts(20));
    }

    @Test
    public void testLongPointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        testIntPointFieldsAtomicUpdates("number_p_l", "long");
        testIntPointFieldsAtomicUpdates("number_p_l_dv", "long");
        testIntPointFieldsAtomicUpdates("number_p_l_dv_ns", "long");
    }

    @Test
    public void testMultiValuedLongPointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        testMultiValuedIntPointFieldsAtomicUpdates("number_p_l_mv", "long");
        testMultiValuedIntPointFieldsAtomicUpdates("number_p_l_ni_mv_dv", "long");
        testMultiValuedIntPointFieldsAtomicUpdates("number_p_l_dv_ns_mv", "long");
    }

    @Test
    public void testLongPointSetQuery() throws Exception {
        doTestSetQueries("number_p_l", getRandomStringArrayWithLongs(20, false), false);
        doTestSetQueries("number_p_l_mv", getRandomStringArrayWithLongs(20, false), true);
        doTestSetQueries("number_p_l_ni_dv", getRandomStringArrayWithLongs(20, false), false);
    }

    @Test
    public void testLongPointFieldNotIndexed() throws Exception {
        doTestFieldNotIndexed("number_p_l_ni", getSequentialStringArrayWithInts(10));
        doTestFieldNotIndexed("number_p_l_ni_mv", getSequentialStringArrayWithInts(10));
    }

    // Date

    @Test
    public void testDatePointFieldExactQuery() throws Exception {
        doTestDatePointFieldExactQuery("number_p_dt", "1995-12-31T23:59:59Z");
        doTestDatePointFieldExactQuery("number_p_dt_mv", "2015-12-31T23:59:59Z-1DAY");
        doTestDatePointFieldExactQuery("number_p_dt_dv", "2000-12-31T23:59:59Z+3DAYS");
        doTestDatePointFieldExactQuery("number_p_dt_mv_dv", "2000-12-31T23:59:59Z+3DAYS");
        doTestDatePointFieldExactQuery("number_p_dt_ni_dv", "2000-12-31T23:59:59Z+3DAYS");
        doTestDatePointFieldExactQuery("number_p_dt_ni_ns_dv", "1995-12-31T23:59:59Z-1MONTH");
        doTestDatePointFieldExactQuery("number_p_dt_ni_mv_dv", "1995-12-31T23:59:59Z+2MONTHS");
    }

    @Test
    public void testDatePointFieldNonSearchableExactQuery() throws Exception {
        doTestDatePointFieldExactQuery("number_p_dt_ni", "1995-12-31T23:59:59Z", false);
        doTestDatePointFieldExactQuery("number_p_dt_ni_ns", "1995-12-31T23:59:59Z", false);

    }

    @Test
    public void testDatePointFieldReturn() throws Exception {
        testPointFieldReturn("number_p_dt", "date", new String[] { "1995-12-31T23:59:59Z", "1994-02-28T23:59:59Z",
                "2015-12-31T23:59:59Z", "2000-10-31T23:59:59Z", "1999-12-31T12:59:59Z" });
        testPointFieldReturn("number_p_dt_dv_ns", "date", new String[] { "1995-12-31T23:59:59Z",
                "1994-02-28T23:59:59Z", "2015-12-31T23:59:59Z", "2000-10-31T23:59:59Z", "1999-12-31T12:59:59Z" });
    }

    @Test
    public void testDatePointFieldRangeQuery() throws Exception {
        doTestDatePointFieldRangeQuery("number_p_dt");
        doTestDatePointFieldRangeQuery("number_p_dt_ni_ns_dv");
    }

    @Test
    public void testDatePointFieldNonSearchableRangeQuery() throws Exception {
        doTestPointFieldNonSearchableRangeQuery("number_p_dt_ni", "1995-12-31T23:59:59Z");
        doTestPointFieldNonSearchableRangeQuery("number_p_dt_ni_ns", "1995-12-31T23:59:59Z");
        doTestPointFieldNonSearchableRangeQuery("number_p_dt_ni_ns_mv", "1995-12-31T23:59:59Z",
                "2000-10-31T23:59:59Z");
    }

    @Test
    public void testDatePointFieldSortAndFunction() throws Exception {
        final SortedSet<String> regexToTest = dynFieldRegexesForType(DatePointField.class);
        final String[] sequential = getSequentialStringArrayWithDates(10);

        for (String r : Arrays.asList("*_p_dt", "*_p_dt_dv", "*_p_dt_dv_ns", "*_p_dt_ni_dv", "*_p_dt_ni_dv_ns",
                "*_p_dt_ni_ns_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSort(r.replace("*", "number"), sequential);
            // TODO: test some randomly generated (then sorted) arrays (with dups and/or missing values)

            doTestDatePointFunctionQuery(r.replace("*", "number"), "date");
        }

        for (String r : Arrays.asList("*_p_dt_ni", "*_p_dt_ni_ns")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "w/o docValues", "1995-12-31T23:59:59Z");

            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "w/o docValues", "1995-12-31T23:59:59Z");
        }

        for (String r : Arrays.asList("*_p_dt_mv", "*_p_dt_ni_mv", "*_p_dt_ni_mv_dv", "*_p_dt_ni_dv_ns_mv",
                "*_p_dt_ni_ns_mv", "*_p_dt_dv_ns_mv", "*_p_dt_mv_dv")) {
            assertTrue(r, regexToTest.remove(r));
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "1995-12-31T23:59:59Z");
            doTestPointFieldSortError(r.replace("*", "number"), "multivalued", "1995-12-31T23:59:59Z",
                    "2000-12-31T23:59:59Z");

            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "1995-12-31T23:59:59Z");
            doTestPointFieldFunctionQueryError(r.replace("*", "number"), "multivalued", "1995-12-31T23:59:59Z",
                    "2000-12-31T23:59:59Z");

        }

        assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);

    }

    @Test
    public void testDatePointFieldFacetField() throws Exception {
        testPointFieldFacetField("number_p_dt", "number_p_dt_dv", getSequentialStringArrayWithDates(10));
        clearIndex();
        assertU(commit());
        testPointFieldFacetField("number_p_dt", "number_p_dt_dv", getSequentialStringArrayWithDates(10));
    }

    @Test
    public void testDatePointFieldRangeFacet() throws Exception {
        doTestDatePointFieldRangeFacet("number_p_dt_dv", "number_p_dt");
    }

    @Test
    public void testDatePointStats() throws Exception {
        testDatePointStats("number_p_dt", "number_p_dt_dv", getSequentialStringArrayWithDates(10));
        testDatePointStats("number_p_dt_mv", "number_p_dt_mv_dv", getSequentialStringArrayWithDates(10));
    }

    @Test
    public void testDatePointFieldMultiValuedExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_dt_mv", getSequentialStringArrayWithDates(20));
        testPointFieldMultiValuedExactQuery("number_p_dt_ni_mv_dv", getSequentialStringArrayWithDates(20));
    }

    @Test
    public void testDatePointFieldMultiValuedNonSearchableExactQuery() throws Exception {
        testPointFieldMultiValuedExactQuery("number_p_dt_ni_mv", getSequentialStringArrayWithDates(20), false);
        testPointFieldMultiValuedExactQuery("number_p_dt_ni_ns_mv", getSequentialStringArrayWithDates(20), false);
    }

    @Test
    public void testDatePointFieldMultiValuedReturn() throws Exception {
        testPointFieldMultiValuedReturn("number_p_dt_mv", "date", getSequentialStringArrayWithDates(20));
        testPointFieldMultiValuedReturn("number_p_dt_ni_mv_dv", "date", getSequentialStringArrayWithDates(20));
        testPointFieldMultiValuedReturn("number_p_dt_dv_ns_mv", "date", getSequentialStringArrayWithDates(20));
    }

    @Test
    public void testDatePointFieldMultiValuedRangeQuery() throws Exception {
        testPointFieldMultiValuedRangeQuery("number_p_dt_mv", "date", getSequentialStringArrayWithDates(20));
        testPointFieldMultiValuedRangeQuery("number_p_dt_ni_mv_dv", "date", getSequentialStringArrayWithDates(20));
    }

    @Test
    public void testDatePointFieldMultiValuedFacetField() throws Exception {
        testPointFieldMultiValuedFacetField("number_p_dt_mv", "number_p_dt_mv_dv",
                getSequentialStringArrayWithDates(20));
        testPointFieldMultiValuedFacetField("number_p_dt_mv", "number_p_dt_mv_dv",
                getRandomStringArrayWithDates(20, false));
    }

    @Test
    public void testDatePointFieldMultiValuedRangeFacet() throws Exception {
        doTestDatePointFieldMultiValuedRangeFacet("number_p_dt_mv_dv", "number_p_dt_mv");
    }

    @Test
    public void testDatePointMultiValuedFunctionQuery() throws Exception {
        testPointMultiValuedFunctionQuery("number_p_dt_mv", "number_p_dt_mv_dv", "date",
                getSequentialStringArrayWithDates(20));
    }

    @Test
    public void testDatePointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        testDatePointFieldsAtomicUpdates("number_p_dt", "date");
        testDatePointFieldsAtomicUpdates("number_p_dt_dv", "date");
        testDatePointFieldsAtomicUpdates("number_p_dt_dv_ns", "date");
    }

    @Test
    public void testMultiValuedDatePointFieldsAtomicUpdates() throws Exception {
        if (!Boolean.getBoolean("enable.update.log")) {
            return;
        }
        testMultiValuedDatePointFieldsAtomicUpdates("number_p_dt_mv", "date");
        testMultiValuedDatePointFieldsAtomicUpdates("number_p_dt_ni_mv_dv", "date");
        testMultiValuedDatePointFieldsAtomicUpdates("number_p_dt_dv_ns_mv", "date");
    }

    @Test
    public void testDatePointSetQuery() throws Exception {
        doTestSetQueries("number_p_dt", getRandomStringArrayWithDates(20, false), false);
        doTestSetQueries("number_p_dt_mv", getRandomStringArrayWithDates(20, false), true);
        doTestSetQueries("number_p_dt_ni_dv", getRandomStringArrayWithDates(20, false), false);
    }

    @Test
    public void testDatePointFieldNotIndexed() throws Exception {
        doTestFieldNotIndexed("number_p_dt_ni", getSequentialStringArrayWithDates(10));
        doTestFieldNotIndexed("number_p_dt_ni_mv", getSequentialStringArrayWithDates(10));
    }

    @Test
    public void testIndexOrDocValuesQuery() throws Exception {
        String[] fieldTypeNames = new String[] { "_p_i", "_p_l", "_p_d", "_p_f" };
        FieldType[] fieldTypes = new FieldType[] { new IntPointField(), new LongPointField(),
                new DoublePointField(), new FloatPointField() };
        assert fieldTypeNames.length == fieldTypes.length;
        for (int i = 0; i < fieldTypeNames.length; i++) {
            SchemaField fieldIndexed = h.getCore().getLatestSchema().getField("foo_" + fieldTypeNames[i]);
            SchemaField fieldIndexedAndDv = h.getCore().getLatestSchema()
                    .getField("foo_" + fieldTypeNames[i] + "_dv");
            SchemaField fieldIndexedMv = h.getCore().getLatestSchema().getField("foo_" + fieldTypeNames[i] + "_mv");
            SchemaField fieldIndexedAndDvMv = h.getCore().getLatestSchema()
                    .getField("foo_" + fieldTypeNames[i] + "_mv_dv");
            assertTrue(fieldTypes[i].getRangeQuery(null, fieldIndexed, "0", "10", true,
                    true) instanceof PointRangeQuery);
            assertTrue(fieldTypes[i].getRangeQuery(null, fieldIndexedAndDv, "0", "10", true,
                    true) instanceof IndexOrDocValuesQuery);
            assertTrue(fieldTypes[i].getRangeQuery(null, fieldIndexedMv, "0", "10", true,
                    true) instanceof PointRangeQuery);
            assertTrue(fieldTypes[i].getRangeQuery(null, fieldIndexedAndDvMv, "0", "10", true,
                    true) instanceof IndexOrDocValuesQuery);
            assertTrue(fieldTypes[i].getFieldQuery(null, fieldIndexed, "0") instanceof PointRangeQuery);
            assertTrue(fieldTypes[i].getFieldQuery(null, fieldIndexedAndDv, "0") instanceof IndexOrDocValuesQuery);
            assertTrue(fieldTypes[i].getFieldQuery(null, fieldIndexedMv, "0") instanceof PointRangeQuery);
            assertTrue(
                    fieldTypes[i].getFieldQuery(null, fieldIndexedAndDvMv, "0") instanceof IndexOrDocValuesQuery);
        }
    }

    public void testInternals() throws IOException {
        String[] types = new String[] { "i", "l", "f", "d" };
        String[] suffixes = new String[] { "", "_dv", "_mv", "_mv_dv", "_ni", "_ni_dv", "_ni_dv_ns", "_ni_dv_ns_mv",
                "_ni_mv", "_ni_mv_dv", "_ni_ns", "_ni_ns_mv", "_dv_ns", "_ni_ns_dv", "_dv_ns_mv" };
        Set<String> typesTested = new HashSet<>();
        for (String type : types) {
            for (String suffix : suffixes) {
                doTestInternals("number_p_" + type + suffix, getSequentialStringArrayWithInts(10));
                typesTested.add("*_p_" + type + suffix);
            }
        }
        for (String suffix : suffixes) {
            doTestInternals("number_p_dt" + suffix, getSequentialStringArrayWithDates(10));
            typesTested.add("*_p_dt" + suffix);
        }

        assertEquals("Missing types in the test", dynFieldRegexesForType(PointField.class), typesTested);
    }

    // Helper methods

    /**
     * Given a FieldType, return the list of DynamicField 'regexes' for all declared 
     * DynamicFields that use that FieldType.
     *
     * @see IndexSchema#getDynamicFields
     * @see DynamicField#getRegex
     */
    private static SortedSet<String> dynFieldRegexesForType(final Class<? extends FieldType> clazz) {
        SortedSet<String> typesToTest = new TreeSet<>();
        for (DynamicField dynField : h.getCore().getLatestSchema().getDynamicFields()) {
            if (clazz.isInstance(dynField.getPrototype().getType())) {
                typesToTest.add(dynField.getRegex());
            }
        }
        return typesToTest;
    }

    private String[] getRandomStringArrayWithDoubles(int length, boolean sorted) {
        Set<Double> set;
        if (sorted) {
            set = new TreeSet<>();
        } else {
            set = new HashSet<>();
        }
        while (set.size() < length) {
            double f = random().nextDouble() * (Double.MAX_VALUE / 2);
            if (random().nextBoolean()) {
                f = f * -1;
            }
            set.add(f);
        }
        String[] stringArr = new String[length];
        int i = 0;
        for (double val : set) {
            stringArr[i] = String.valueOf(val);
            i++;
        }
        return stringArr;
    }

    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() * (Float.MAX_VALUE / 2);
            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[] getSequentialStringArrayWithDates(int length) {
        assert length < 60;
        String[] arr = new String[length];
        for (int i = 0; i < length; i++) {
            arr[i] = String.format(Locale.ROOT, "1995-12-11T19:59:%02dZ", 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;
    }

    private String[] getRandomStringArrayWithLongs(int length, boolean sorted) {
        Set<Long> set;
        if (sorted) {
            set = new TreeSet<>();
        } else {
            set = new HashSet<>();
        }
        while (set.size() < length) {
            long number = random().nextLong();
            if (random().nextBoolean()) {
                number = number * -1;
            }
            set.add(number);
        }
        String[] stringArr = new String[length];
        int i = 0;
        for (long val : set) {
            stringArr[i] = String.valueOf(val);
            i++;
        }
        return stringArr;
    }

    private String[] getRandomStringArrayWithDates(int length, boolean sorted) {
        assert length < 60;
        Set<Integer> set;
        if (sorted) {
            set = new TreeSet<>();
        } else {
            set = new HashSet<>();
        }
        while (set.size() < length) {
            int number = random().nextInt(60);
            set.add(number);
        }
        String[] stringArr = new String[length];
        int i = 0;
        for (int val : set) {
            stringArr[i] = String.format(Locale.ROOT, "1995-12-11T19:59:%02dZ", val);
            i++;
        }
        return stringArr;
    }

    private void doTestFieldNotIndexed(String field, String[] values) throws IOException {
        assert values.length == 10;
        // test preconditions
        SchemaField sf = h.getCore().getLatestSchema().getField(field);
        assertFalse("Field should be indexed=false", sf.indexed());
        assertFalse("Field should be docValues=false", sf.hasDocValues());

        for (int i = 0; i < 10; i++) {
            assertU(adoc("id", String.valueOf(i), field, values[i]));
        }
        assertU(commit());
        assertQ(req("q", "*:*"), "//*[@numFound='10']");
        assertQ("Can't search on index=false docValues=false field", req("q", field + ":[* TO *]"),
                "//*[@numFound='0']");
        IndexReader ir;
        RefCounted<SolrIndexSearcher> ref = null;
        try {
            ref = h.getCore().getSearcher();
            ir = ref.get().getIndexReader();
            assertEquals("Field " + field + " should have no point values", 0, PointValues.size(ir, field));
        } finally {
            ref.decref();
        }
    }

    private void doTestIntPointFieldExactQuery(final String field, final boolean testLong) throws Exception {
        doTestIntPointFieldExactQuery(field, testLong, true);
    }

    /**
     * @param field the field to use for indexing and searching against
     * @param testLong set to true if "field" is expected to support long values, false if only integers
     * @param searchable set to true if searches against "field" should succeed, false if field is only stored and searches should always get numFound=0
     */
    private void doTestIntPointFieldExactQuery(final String field, final boolean testLong, final boolean searchable)
            throws Exception {
        final String MATCH_ONE = "//*[@numFound='" + (searchable ? "1" : "0") + "']";
        final String MATCH_TWO = "//*[@numFound='" + (searchable ? "2" : "0") + "']";

        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), MATCH_ONE);
        }

        for (int i = 0; i < 10; i++) {
            assertQ(req("debug", "true", "q", field + ":" + (i + 1) + " OR " + field + ":" + ((i + 1) % 10 + 1)),
                    MATCH_TWO);
        }

        assertU(adoc("id", String.valueOf(Integer.MAX_VALUE), field, String.valueOf(Integer.MAX_VALUE)));
        assertU(commit());
        assertQ(req("q", field + ":" + Integer.MAX_VALUE, "fl", "id, " + field), MATCH_ONE);

        if (testLong) {
            for (long i = (long) Integer.MAX_VALUE; i < (long) Integer.MAX_VALUE + 10; i++) {
                assertU(adoc("id", String.valueOf(i), field, String.valueOf(i + 1)));
            }
            assertU(commit());
            for (long i = (long) Integer.MAX_VALUE; i < (long) Integer.MAX_VALUE + 10; i++) {
                assertQ(req("q", field + ":" + (i + 1), "fl", "id, " + field), MATCH_ONE);
            }
            assertU(adoc("id", String.valueOf(Long.MAX_VALUE), field, String.valueOf(Long.MAX_VALUE)));
            assertU(commit());
            assertQ(req("q", field + ":" + Long.MAX_VALUE, "fl", "id, " + field), MATCH_ONE);
        }

        clearIndex();
        assertU(commit());
    }

    private void testPointFieldReturn(String field, String type, String[] values) throws Exception {
        SchemaField sf = h.getCore().getLatestSchema().getField(field);
        assert sf.stored() || (sf.hasDocValues() && sf.useDocValuesAsStored()) : "Unexpected field definition for "
                + field;
        for (int i = 0; i < values.length; i++) {
            assertU(adoc("id", String.valueOf(i), field, values[i]));
        }
        // Check using RTG
        if (Boolean.getBoolean("enable.update.log")) {
            for (int i = 0; i < values.length; i++) {
                assertQ(req("qt", "/get", "id", String.valueOf(i)),
                        "//doc/" + type + "[@name='" + field + "'][.='" + values[i] + "']");
            }
        }
        assertU(commit());
        String[] expected = new String[values.length + 1];
        expected[0] = "//*[@numFound='" + values.length + "']";
        for (int i = 0; i < values.length; i++) {
            expected[i + 1] = "//result/doc[str[@name='id']='" + i + "']/" + type + "[@name='" + field + "'][.='"
                    + values[i] + "']";
        }
        assertQ(req("q", "*:*", "fl", "id, " + field, "rows", String.valueOf(values.length)), expected);

        // Check using RTG
        if (Boolean.getBoolean("enable.update.log")) {
            for (int i = 0; i < values.length; i++) {
                assertQ(req("qt", "/get", "id", String.valueOf(i)),
                        "//doc/" + type + "[@name='" + field + "'][.='" + values[i] + "']");
            }
        }
        clearIndex();
        assertU(commit());
    }

    private void doTestPointFieldNonSearchableRangeQuery(String fieldName, String... values) throws Exception {
        for (int i = 9; i >= 0; i--) {
            SolrInputDocument doc = sdoc("id", String.valueOf(i));
            for (String value : values) {
                doc.addField(fieldName, value);
            }
            assertU(adoc(doc));
        }
        assertU(commit());
        assertQ(req("q", fieldName + ":[* TO *]", "fl", "id, " + fieldName, "sort", "id asc"),
                "//*[@numFound='0']");
    }

    private void doTestIntPointFieldRangeQuery(String fieldName, String type, boolean testLong) throws Exception {
        for (int i = 9; i >= 0; i--) {
            assertU(adoc("id", String.valueOf(i), fieldName, String.valueOf(i)));
        }
        assertU(commit());
        assertQ(req("q", fieldName + ":[0 TO 3]", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='4']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='2']",
                "//result/doc[4]/" + type + "[@name='" + fieldName + "'][.='3']");

        assertQ(req("q", fieldName + ":{0 TO 3]", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='2']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='3']");

        assertQ(req("q", fieldName + ":[0 TO 3}", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='2']");

        assertQ(req("q", fieldName + ":{0 TO 3}", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='2']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='2']");

        assertQ(req("q", fieldName + ":{0 TO *}", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='9']",
                "0=count(//result/doc/" + type + "[@name='" + fieldName + "'][.='0'])",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1']");

        assertQ(req("q", fieldName + ":{* TO 3}", "fl", "id, " + fieldName, "sort", "id desc"),
                "//*[@numFound='3']", "0=count(//result/doc/" + type + "[@name='" + fieldName + "'][.='3'])",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='2']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='0']");

        assertQ(req("q", fieldName + ":[* TO 3}", "fl", "id, " + fieldName, "sort", "id desc"),
                "//*[@numFound='3']", "0=count(//result/doc/" + type + "[@name='" + fieldName + "'][.='3'])",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='2']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='0']");

        assertQ(req("q", fieldName + ":[* TO *}", "fl", "id, " + fieldName, "sort", "id asc"),
                "//*[@numFound='10']", "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0']",
                "//result/doc[10]/" + type + "[@name='" + fieldName + "'][.='9']");

        assertQ(req("q", fieldName + ":[0 TO 1] OR " + fieldName + ":[8 TO 9]", "fl", "id, " + fieldName, "sort",
                "id asc"), "//*[@numFound='4']", "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='8']",
                "//result/doc[4]/" + type + "[@name='" + fieldName + "'][.='9']");

        assertQ(req("q", fieldName + ":[0 TO 1] AND " + fieldName + ":[1 TO 2]", "fl", "id, " + fieldName),
                "//*[@numFound='1']", "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1']");

        assertQ(req("q", fieldName + ":[0 TO 1] AND NOT " + fieldName + ":[1 TO 2]", "fl", "id, " + fieldName),
                "//*[@numFound='1']", "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0']");

        clearIndex();
        assertU(commit());

        String[] arr;
        if (testLong) {
            arr = getRandomStringArrayWithLongs(100, true);
        } else {
            arr = getRandomStringArrayWithInts(100, 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)) + "']");
            assertQ(req("q", fieldName + ":[" + arr[0] + " TO " + arr[i] + "] AND " + fieldName + ":"
                    + arr[0].replace("-", "\\-"), "fl", "id, " + fieldName), "//*[@numFound='1']");
        }
    }

    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']");

        //    assertU(commit());
        //    assertQ(req("q", "id:0", "fl", "id, " + docValuesField, "facet", "true", "facet.field", docValuesField, "facet.mincount", "0"), 
        //        "//*[@numFound='1']",
        //        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[0] + "'][.='1']",
        //        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[1] + "'][.='0']",
        //        "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[2] + "'][.='0']",
        //        "count(//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int))==10");

        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);
    }

    private void doTestIntPointFieldRangeFacet(String docValuesField, String nonDocValuesField) throws Exception {
        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 PointField);
        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 PointField);
        // 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']");
    }

    private void doTestIntPointFunctionQuery(String field, String type) throws Exception {
        for (int i = 9; i >= 0; i--) {
            assertU(adoc("id", String.valueOf(i), field, String.valueOf(i)));
        }
        assertU(commit());
        assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);

        assertQ(req("q", "*:*", "fl", "id, " + field, "sort", "product(-1," + field + ") asc"),
                "//*[@numFound='10']", "//result/doc[1]/" + type + "[@name='" + field + "'][.='9']",
                "//result/doc[2]/" + type + "[@name='" + field + "'][.='8']",
                "//result/doc[3]/" + type + "[@name='" + field + "'][.='7']",
                "//result/doc[10]/" + type + "[@name='" + field + "'][.='0']");

        assertQ(req("q", "*:*", "fl", "id, " + field + ", product(-1," + field + ")", "sort", "id asc"),
                "//*[@numFound='10']", "//result/doc[1]/float[@name='product(-1," + field + ")'][.='-0.0']",
                "//result/doc[2]/float[@name='product(-1," + field + ")'][.='-1.0']",
                "//result/doc[3]/float[@name='product(-1," + field + ")'][.='-2.0']",
                "//result/doc[10]/float[@name='product(-1," + field + ")'][.='-9.0']");

        assertQ(req("q", "*:*", "fl", "id, " + field + ", field(" + field + ")", "sort", "id asc"),
                "//*[@numFound='10']", "//result/doc[1]/" + type + "[@name='field(" + field + ")'][.='0']",
                "//result/doc[2]/" + type + "[@name='field(" + field + ")'][.='1']",
                "//result/doc[3]/" + type + "[@name='field(" + field + ")'][.='2']",
                "//result/doc[10]/" + type + "[@name='field(" + field + ")'][.='9']");

    }

    /** 
     * Checks that the specified field can not be used as a value source, even if there are documents 
     * with (all) the specified values in the index.
     *
     * @param field the field name to try and sort on
     * @param errSubStr substring to look for in the error msg
     * @param values one or more values to put into the doc(s) in the index - may be more then one for multivalued fields
     */
    private void doTestPointFieldFunctionQueryError(String field, String errSubStr, String... values)
            throws Exception {
        final int numDocs = atLeast(random(), 10);
        for (int i = 0; i < numDocs; i++) {
            SolrInputDocument doc = sdoc("id", String.valueOf(i));
            for (String v : values) {
                doc.addField(field, v);
            }
            assertU(adoc(doc));
        }

        assertQEx("Should not be able to use field in function: " + field, errSubStr,
                req("q", "*:*", "fl", "id", "fq", "{!frange l=0 h=100}product(-1, " + field + ")"),
                SolrException.ErrorCode.BAD_REQUEST);

        clearIndex();
        assertU(commit());

        // empty index should (also) give same error
        assertQEx("Should not be able to use field in function: " + field, errSubStr,
                req("q", "*:*", "fl", "id", "fq", "{!frange l=0 h=100}product(-1, " + field + ")"),
                SolrException.ErrorCode.BAD_REQUEST);

    }

    private void testPointStats(String field, String dvField, String[] numbers, double min, double max,
            String count, String missing, double delta) {
        String minMin = String.valueOf(min - Math.abs(delta * min));
        String maxMin = String.valueOf(min + Math.abs(delta * min));
        String minMax = String.valueOf(max - Math.abs(delta * max));
        String maxMax = String.valueOf(max + Math.abs(delta * max));
        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'][.>='" + minMin + "']",
                "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField
                        + "']/double[@name='min'][.<='" + maxMin + "']",
                "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField
                        + "']/double[@name='max'][.>='" + minMax + "']",
                "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField
                        + "']/double[@name='max'][.<='" + maxMax + "']",
                "//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);
    }

    private void testPointFieldMultiValuedExactQuery(final String fieldName, final String[] numbers)
            throws Exception {
        testPointFieldMultiValuedExactQuery(fieldName, numbers, true);
    }

    /**
     * @param fieldName the field to use for indexing and searching against
     * @param numbers list of 20 values to index in 10 docs (pairwise)
     * @param searchable set to true if searches against "field" should succeed, false if field is only stored and searches should always get numFound=0
     */
    private void testPointFieldMultiValuedExactQuery(final String fieldName, final String[] numbers,
            final boolean searchable) throws Exception {

        final String MATCH_ONE = "//*[@numFound='" + (searchable ? "1" : "0") + "']";
        final String MATCH_TWO = "//*[@numFound='" + (searchable ? "2" : "0") + "']";

        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++) {
            if (h.getCore().getLatestSchema().getField(fieldName).getType() instanceof DatePointField) {
                assertQ(req("q", fieldName + ":\"" + numbers[i] + "\""), MATCH_ONE);
            } else {
                assertQ(req("q", fieldName + ":" + numbers[i].replace("-", "\\-")), MATCH_ONE);
            }
        }

        for (int i = 0; i < 20; i++) {
            if (h.getCore().getLatestSchema().getField(fieldName).getType() instanceof DatePointField) {
                assertQ(req("q", fieldName + ":\"" + numbers[i] + "\"" + " OR " + fieldName + ":\""
                        + numbers[(i + 1) % 10] + "\""), MATCH_TWO);
            } else {
                assertQ(req("q", fieldName + ":" + numbers[i].replace("-", "\\-") + " OR " + fieldName + ":"
                        + numbers[(i + 1) % 10].replace("-", "\\-")), MATCH_TWO);
            }
        }
    }

    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 = 9; i >= 0; i--) {
            assertU(adoc("id", String.valueOf(i), fieldName, numbers[i], fieldName, numbers[i + 10]));
        }
        // Check using RTG before commit
        if (Boolean.getBoolean("enable.update.log")) {
            for (int i = 0; i < 10; i++) {
                assertQ(req("qt", "/get", "id", String.valueOf(i)),
                        "//doc/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[i] + "']",
                        "//doc/arr[@name='" + fieldName + "']/" + type + "[2][.='" + numbers[i + 10] + "']",
                        "count(//doc/arr[@name='" + fieldName + "']/" + type + ")=2");
            }
        }
        // Check using RTG after commit
        assertU(commit());
        if (Boolean.getBoolean("enable.update.log")) {
            for (int i = 0; i < 10; i++) {
                assertQ(req("qt", "/get", "id", String.valueOf(i)),
                        "//doc/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[i] + "']",
                        "//doc/arr[@name='" + fieldName + "']/" + type + "[2][.='" + numbers[i + 10] + "']",
                        "count(//doc/arr[@name='" + fieldName + "']/" + type + ")=2");
            }
        }
        String[] expected = new String[21];
        expected[0] = "//*[@numFound='10']";
        for (int i = 1; i <= 10; i++) {
            // checks for each doc's two values aren't next to eachother in array, but that doesn't matter for correctness
            expected[i] = "//result/doc[" + i + "]/arr[@name='" + fieldName + "']/" + type + "[1][.='"
                    + numbers[i - 1] + "']";
            expected[i + 10] = "//result/doc[" + i + "]/arr[@name='" + fieldName + "']/" + type + "[2][.='"
                    + numbers[i + 9] + "']";
        }
        assertQ(req("q", "*:*", "fl", "id, " + fieldName, "sort", "id asc"), expected);
    }

    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 = 9; i >= 0; i--) {
            assertU(adoc("id", String.valueOf(i), fieldName, numbers[i], fieldName, numbers[i + 10]));
        }
        assertU(commit());
        assertQ(req("q", String.format(Locale.ROOT, "%s:[%s TO %s]", fieldName, numbers[0], numbers[3]), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@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", String.format(Locale.ROOT, "%s:{%s TO %s]", fieldName, numbers[0], numbers[3]), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@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", String.format(Locale.ROOT, "%s:[%s TO %s}", fieldName, numbers[0], numbers[3]), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@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", String.format(Locale.ROOT, "%s:{%s TO %s}", fieldName, numbers[0], numbers[3]), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@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", String.format(Locale.ROOT, "%s:{%s TO *}", fieldName, numbers[0]), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='10']",
                "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']");

        assertQ(req("q", String.format(Locale.ROOT, "%s:{%s TO *}", fieldName, numbers[10]), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='9']",
                "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[1] + "']");

        assertQ(req("q", String.format(Locale.ROOT, "%s:{* TO %s}", fieldName, numbers[3]), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']");

        assertQ(req("q", String.format(Locale.ROOT, "%s:[* TO %s}", fieldName, numbers[3]), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']");

        assertQ(req("q", fieldName + ":[* TO *}", "fl", "id, " + fieldName, "sort", "id asc"),
                "//*[@numFound='10']",
                "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']",
                "//result/doc[10]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[9] + "']");

        assertQ(req("q",
                String.format(Locale.ROOT, "%s:[%s TO %s] OR %s:[%s TO %s]", fieldName, numbers[0], numbers[1],
                        fieldName, numbers[8], numbers[9]),
                "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='4']",
                "//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[8] + "']",
                "//result/doc[4]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[9] + "']");

        assertQ(req("q",
                String.format(Locale.ROOT, "%s:[%s TO %s] OR %s:[%s TO %s]", fieldName, numbers[0], numbers[0],
                        fieldName, numbers[10], numbers[10]),
                "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='1']",
                "//result/doc[1]/arr[@name='" + fieldName + "']/" + type + "[1][.='" + numbers[0] + "']");
    }

    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]));
            if (rarely()) {
                assertU(commit());
            }
        }
        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']");

        assertU(adoc("id", "10", dvFieldName, numbers[1], nonDocValuesField, numbers[1], dvFieldName, numbers[1],
                nonDocValuesField, numbers[1]));
        assertU(commit());
        assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "facet", "true", "facet.field", dvFieldName,
                "facet.missing", "true"), "//*[@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']",
                "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName
                        + "']/int[not(@name)][.='0']");

        assertU(adoc("id", "10")); // add missing values
        assertU(commit());
        assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "facet", "true", "facet.field", dvFieldName,
                "facet.missing", "true"), "//*[@numFound='11']",
                "//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[not(@name)][.='1']");

        assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "facet", "true", "facet.field", dvFieldName,
                "facet.mincount", "3"), "//*[@numFound='11']",
                "count(//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName
                        + "']/int)=0");

        assertQ(req("q", "id:0", "fl", "id, " + dvFieldName, "facet", "true", "facet.field", dvFieldName),
                "//*[@numFound='1']",
                "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName + "']/int[@name='"
                        + numbers[0] + "'][.='1']",
                "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName + "']/int[@name='"
                        + numbers[10] + "'][.='1']",
                "count(//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName
                        + "']/int)=2");

        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);
        clearIndex();
        assertU(commit());

        String smaller, larger;
        try {
            if (Long.parseLong(numbers[1]) < Long.parseLong(numbers[2])) {
                smaller = numbers[1];
                larger = numbers[2];
            } else {
                smaller = numbers[2];
                larger = numbers[1];
            }
        } catch (NumberFormatException e) {
            try {
                if (Double.valueOf(numbers[1]) < Double.valueOf(numbers[2])) {
                    smaller = numbers[1];
                    larger = numbers[2];
                } else {
                    smaller = numbers[2];
                    larger = numbers[1];
                }
            } catch (NumberFormatException e2) {
                if (DateMathParser.parseMath(null, numbers[1]).getTime() < DateMathParser
                        .parseMath(null, numbers[2]).getTime()) {
                    smaller = numbers[1];
                    larger = numbers[2];
                } else {
                    smaller = numbers[2];
                    larger = numbers[1];
                }
            }
        }

        assertU(adoc("id", "1", dvFieldName, smaller, dvFieldName, larger));
        assertU(adoc("id", "2", dvFieldName, larger));
        assertU(commit());

        assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "facet", "true", "facet.field", dvFieldName),
                "//*[@numFound='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName + "']/int[@name='"
                        + larger + "'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName + "']/int[@name='"
                        + smaller + "'][.='1']",
                "count(//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName
                        + "']/int)=2");

        assertQ(req("q", "*:*", "fl", "id, " + dvFieldName, "facet", "true", "facet.field", dvFieldName,
                "facet.sort", "index"), "//*[@numFound='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName + "']/int[@name='"
                        + smaller + "'][.='1']",
                "//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName + "']/int[@name='"
                        + larger + "'][.='2']",
                "count(//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + dvFieldName
                        + "']/int)=2");

        clearIndex();
        assertU(commit());

    }

    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] + "']");

        assertQ(req("q", "*:*", "fl", "id, " + docValuesField, "sort", function + " 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(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 void testMultiValuedIntPointFieldsAtomicUpdates(String field, String type) throws Exception {
        assertU(adoc(sdoc("id", "1", field, "1")));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='1']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=1");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("add", 2))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='1']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='2']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=2");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("remove", 1))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='2']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=1");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", ImmutableList.of(1, 2, 3)))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='1']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='2']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='3']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=3");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("removeregex", ".*"))));
        assertU(commit());

        assertQ(req("q", "id:1"), "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=0");

    }

    private void testMultiValuedFloatPointFieldsAtomicUpdates(String field, String type) throws Exception {
        assertU(adoc(sdoc("id", "1", field, "1.0")));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='1.0']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=1");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("add", 2.1f))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='1.0']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='2.1']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=2");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("remove", 1f))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='2.1']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=1");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", ImmutableList.of(1f, 2f, 3f)))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='1.0']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='2.0']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='3.0']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=3");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("removeregex", ".*"))));
        assertU(commit());

        assertQ(req("q", "id:1"), "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=0");

    }

    private void testIntPointFieldsAtomicUpdates(String field, String type) throws Exception {
        assertU(adoc(sdoc("id", "1", field, "1")));
        assertU(commit());

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", 1))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.='2']");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", -1))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.='1']");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", 3))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.='3']");
    }

    private void doTestFloatPointFieldExactQuery(final String field) throws Exception {
        doTestFloatPointFieldExactQuery(field, true);
    }

    /**
     * @param field the field to use for indexing and searching against
     * @param searchable set to true if searches against "field" should succeed, false if field is only stored and searches should always get numFound=0
     */
    private void doTestFloatPointFieldExactQuery(String field, final boolean searchable) throws Exception {
        final String MATCH_ONE = "//*[@numFound='" + (searchable ? "1" : "0") + "']";
        final String MATCH_TWO = "//*[@numFound='" + (searchable ? "2" : "0") + "']";

        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), MATCH_ONE);
        }

        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), MATCH_TWO);
        }

        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), MATCH_ONE);
        }
        clearIndex();
        assertU(commit());
    }

    /**
     * For each value, creates a doc with that value in the specified field and then asserts that
     * asc/desc sorts on that field succeeds and that the docs are in the (relatively) expected order
     *
     * @param field name of field to sort on
     * @param values list of values in ascending order
     */
    private void doTestPointFieldSort(String field, String... values) throws Exception {
        assert values != null && 2 <= values.length;

        // TODO: need to add sort missing coverage...
        //
        // idea: accept "null" as possible value for sort missing tests ?
        //
        // need to account for possibility that multiple nulls will be in non deterministic order
        // always using secondary sort on id seems prudent ... handles any "dups" in values[]

        final List<SolrInputDocument> docs = new ArrayList<>(values.length);
        final String[] ascXpathChecks = new String[values.length + 1];
        final String[] descXpathChecks = new String[values.length + 1];
        ascXpathChecks[values.length] = "//*[@numFound='" + values.length + "']";
        descXpathChecks[values.length] = "//*[@numFound='" + values.length + "']";

        for (int i = values.length - 1; i >= 0; i--) {
            docs.add(sdoc("id", String.valueOf(i), field, String.valueOf(values[i])));
            // reminder: xpath array indexes start at 1
            ascXpathChecks[i] = "//result/doc[" + (1 + i) + "]/str[@name='id'][.='" + i + "']";
            descXpathChecks[i] = "//result/doc[" + (values.length - i) + "]/str[@name='id'][.='" + i + "']";
        }

        // ensure doc add order doesn't affect results
        Collections.shuffle(docs, random());
        for (SolrInputDocument doc : docs) {
            assertU(adoc(doc));
        }
        assertU(commit());

        assertQ(req("q", "*:*", "fl", "id", "sort", field + " asc"), ascXpathChecks);
        assertQ(req("q", "*:*", "fl", "id", "sort", field + " desc"), descXpathChecks);

        clearIndex();
        assertU(commit());
    }

    /** 
     * Checks that the specified field can not be sorted on, even if there are documents 
     * with (all) the specified values in the index.
     *
     * @param field the field name to try and sort on
     * @param errSubStr substring to look for in the error msg
     * @param values one or more values to put into the doc(s) in the index - may be more then one for multivalued fields
     */
    private void doTestPointFieldSortError(String field, String errSubStr, String... values) throws Exception {

        final int numDocs = atLeast(random(), 10);
        for (int i = 0; i < numDocs; i++) {
            SolrInputDocument doc = sdoc("id", String.valueOf(i));
            for (String v : values) {
                doc.addField(field, v);
            }
            assertU(adoc(doc));
        }

        assertQEx("Should not be able to sort on field: " + field, errSubStr,
                req("q", "*:*", "fl", "id", "sort", field + " desc"), SolrException.ErrorCode.BAD_REQUEST);

        clearIndex();
        assertU(commit());

        // empty index should (also) give same error
        assertQEx("Should not be able to sort on field: " + field, errSubStr,
                req("q", "*:*", "fl", "id", "sort", field + " desc"), SolrException.ErrorCode.BAD_REQUEST);

    }

    private void doTestFloatPointFieldRangeQuery(String fieldName, String type, boolean testDouble)
            throws Exception {
        for (int i = 9; i >= 0; i--) {
            assertU(adoc("id", String.valueOf(i), fieldName, String.valueOf(i)));
        }
        assertU(commit());
        assertQ(req("q", fieldName + ":[0 TO 3]", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='4']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0.0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1.0']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='2.0']",
                "//result/doc[4]/" + type + "[@name='" + fieldName + "'][.='3.0']");

        assertQ(req("q", fieldName + ":{0 TO 3]", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1.0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='2.0']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='3.0']");

        assertQ(req("q", fieldName + ":[0 TO 3}", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0.0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1.0']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='2.0']");

        assertQ(req("q", fieldName + ":{0 TO 3}", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='2']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1.0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='2.0']");

        assertQ(req("q", fieldName + ":{0 TO *}", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='9']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1.0']");

        assertQ(req("q", fieldName + ":{* TO 3}", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0.0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1.0']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='2.0']");

        assertQ(req("q", fieldName + ":[* TO 3}", "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0.0']",
                "//result/doc[2]/" + type + "[@name='" + fieldName + "'][.='1.0']",
                "//result/doc[3]/" + type + "[@name='" + fieldName + "'][.='2.0']");

        assertQ(req("q", fieldName + ":[* TO *}", "fl", "id, " + fieldName, "sort", "id asc"),
                "//*[@numFound='10']", "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='0.0']",
                "//result/doc[10]/" + type + "[@name='" + fieldName + "'][.='9.0']");

        assertQ(req("q", fieldName + ":[0.9 TO 1.01]", "fl", "id, " + fieldName), "//*[@numFound='1']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1.0']");

        assertQ(req("q", fieldName + ":{0.9 TO 1.01}", "fl", "id, " + fieldName), "//*[@numFound='1']",
                "//result/doc[1]/" + type + "[@name='" + fieldName + "'][.='1.0']");

        clearIndex();
        assertU(commit());

        String[] arr;
        if (testDouble) {
            arr = getRandomStringArrayWithDoubles(10, true);
        } else {
            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)) + "']");
        }
    }

    private void doTestFloatPointFieldRangeFacet(String docValuesField, String nonDocValuesField) throws Exception {

        for (int i = 0; i < 10; i++) {
            assertU(adoc("id", String.valueOf(i), docValuesField, String.format(Locale.ROOT, "%f", (float) i * 1.1),
                    nonDocValuesField, String.format(Locale.ROOT, "%f", (float) i * 1.1)));
        }
        assertU(commit());
        assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
        assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
        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 PointField);
        // 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']");
    }

    private void doTestFloatPointFunctionQuery(String field, String type) throws Exception {
        for (int i = 9; i >= 0; i--) {
            assertU(adoc("id", String.valueOf(i), field, String.format(Locale.ROOT, "%f", (float) i * 1.1)));
        }
        assertU(commit());
        assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
        assertQ(req("q", "*:*", "fl", "id, " + field, "sort", "product(-1," + field + ") asc"),
                "//*[@numFound='10']", "//result/doc[1]/" + type + "[@name='" + field + "'][.='9.9']",
                "//result/doc[2]/" + type + "[@name='" + field + "'][.='8.8']",
                "//result/doc[3]/" + type + "[@name='" + field + "'][.='7.7']",
                "//result/doc[10]/" + type + "[@name='" + field + "'][.='0.0']");

        assertQ(req("q", "*:*", "fl", "id, " + field + ", product(-1," + field + ")", "sort", "id asc"),
                "//*[@numFound='10']", "//result/doc[1]/float[@name='product(-1," + field + ")'][.='-0.0']",
                "//result/doc[2]/float[@name='product(-1," + field + ")'][.='-1.1']",
                "//result/doc[3]/float[@name='product(-1," + field + ")'][.='-2.2']",
                "//result/doc[10]/float[@name='product(-1," + field + ")'][.='-9.9']");

        assertQ(req("q", "*:*", "fl", "id, " + field + ", field(" + field + ")", "sort", "id asc"),
                "//*[@numFound='10']", "//result/doc[1]/" + type + "[@name='field(" + field + ")'][.='0.0']",
                "//result/doc[2]/" + type + "[@name='field(" + field + ")'][.='1.1']",
                "//result/doc[3]/" + type + "[@name='field(" + field + ")'][.='2.2']",
                "//result/doc[10]/" + type + "[@name='field(" + field + ")'][.='9.9']");
    }

    private void doTestSetQueries(String fieldName, String[] values, boolean multiValued) {
        for (int i = 0; i < values.length; i++) {
            assertU(adoc("id", String.valueOf(i), fieldName, values[i]));
        }
        assertU(commit());
        SchemaField sf = h.getCore().getLatestSchema().getField(fieldName);
        assertTrue(sf.getType() instanceof PointField);

        for (int i = 0; i < values.length; i++) {
            assertQ(req("q", "{!term f='" + fieldName + "'}" + values[i], "fl", "id," + fieldName),
                    "//*[@numFound='1']");
        }

        for (int i = 0; i < values.length; i++) {
            assertQ(req("q", "{!terms f='" + fieldName + "'}" + values[i] + "," + values[(i + 1) % values.length],
                    "fl", "id," + fieldName), "//*[@numFound='2']");
        }

        assertTrue(values.length > SolrQueryParser.TERMS_QUERY_THRESHOLD);
        int numTerms = SolrQueryParser.TERMS_QUERY_THRESHOLD + 1;
        StringBuilder builder = new StringBuilder(fieldName + ":(");
        for (int i = 0; i < numTerms; i++) {
            if (sf.getType().getNumberType() == NumberType.DATE) {
                builder.append(String.valueOf(values[i]).replace(":", "\\:") + ' ');
            } else {
                builder.append(String.valueOf(values[i]).replace("-", "\\-") + ' ');
            }
        }
        builder.append(')');
        if (sf.indexed()) { // SolrQueryParser should also be generating a PointInSetQuery if indexed
            assertQ(req(CommonParams.DEBUG, CommonParams.QUERY, "q", "*:*", "fq", builder.toString(), "fl",
                    "id," + fieldName), "//*[@numFound='" + numTerms + "']",
                    "//*[@name='parsed_filter_queries']/str[.='(" + getSetQueryToString(fieldName, values, numTerms)
                            + ")']");
        } else {
            // Won't use PointInSetQuery if the fiels is not indexed, but should match the same docs
            assertQ(req(CommonParams.DEBUG, CommonParams.QUERY, "q", "*:*", "fq", builder.toString(), "fl",
                    "id," + fieldName), "//*[@numFound='" + numTerms + "']");
        }

        if (multiValued) {
            clearIndex();
            assertU(commit());
            for (int i = 0; i < values.length; i++) {
                assertU(adoc("id", String.valueOf(i), fieldName, values[i], fieldName,
                        values[(i + 1) % values.length]));
            }
            assertU(commit());
            for (int i = 0; i < values.length; i++) {
                assertQ(req("q", "{!term f='" + fieldName + "'}" + values[i], "fl", "id," + fieldName),
                        "//*[@numFound='2']");
            }

            for (int i = 0; i < values.length; i++) {
                assertQ(req("q",
                        "{!terms f='" + fieldName + "'}" + values[i] + "," + values[(i + 1) % values.length], "fl",
                        "id," + fieldName), "//*[@numFound='3']");
            }
        }
    }

    private String getSetQueryToString(String fieldName, String[] values, int numTerms) {
        SchemaField sf = h.getCore().getLatestSchema().getField(fieldName);
        return sf.getType().getSetQuery(null, sf, Arrays.asList(Arrays.copyOf(values, numTerms))).toString();
    }

    private void doTestDoublePointFieldMultiValuedRangeFacet(String docValuesField, String nonDocValuesField)
            throws Exception {
        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 PointField);
        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 PointField);
        // 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']");
    }

    private void doTestIntPointFieldMultiValuedRangeFacet(String docValuesField, String nonDocValuesField)
            throws Exception {
        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 PointField);
        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 PointField);
        // 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 doTestDatePointFieldExactQuery(final String field, final String baseDate) throws Exception {
        doTestDatePointFieldExactQuery(field, baseDate, true);
    }

    /**
     * @param field the field to use for indexing and searching against
     * @param baseDate basic value to use for indexing and searching
     * @param searchable set to true if searches against "field" should succeed, false if field is only stored and searches should always get numFound=0
     */
    private void doTestDatePointFieldExactQuery(final String field, final String baseDate, final boolean searchable)
            throws Exception {
        final String MATCH_ONE = "//*[@numFound='" + (searchable ? "1" : "0") + "']";
        final String MATCH_TWO = "//*[@numFound='" + (searchable ? "2" : "0") + "']";

        for (int i = 0; i < 10; i++) {
            assertU(adoc("id", String.valueOf(i), field,
                    String.format(Locale.ROOT, "%s+%dMINUTES", baseDate, i + 1)));
        }
        assertU(commit());
        for (int i = 0; i < 10; i++) {
            String date = String.format(Locale.ROOT, "%s+%dMINUTES", baseDate, i + 1);
            assertQ(req("q", field + ":\"" + date + "\"", "fl", "id, " + field), MATCH_ONE);
        }

        for (int i = 0; i < 10; i++) {
            String date1 = String.format(Locale.ROOT, "%s+%dMINUTES", baseDate, i + 1);
            String date2 = String.format(Locale.ROOT, "%s+%dMINUTES", baseDate, ((i + 1) % 10 + 1));
            assertQ(req("q", field + ":\"" + date1 + "\"" + " OR " + field + ":\"" + date2 + "\""), MATCH_TWO);
        }

        clearIndex();
        assertU(commit());
    }

    private void doTestDatePointFieldRangeQuery(String fieldName) throws Exception {
        String baseDate = "1995-12-31T10:59:59Z";
        for (int i = 9; i >= 0; i--) {
            assertU(adoc("id", String.valueOf(i), fieldName,
                    String.format(Locale.ROOT, "%s+%dHOURS", baseDate, i)));
        }
        assertU(commit());
        assertQ(req("q",
                fieldName + ":" + String.format(Locale.ROOT, "[%s+0HOURS TO %s+3HOURS]", baseDate, baseDate), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='4']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T10:59:59Z']",
                "//result/doc[2]/date[@name='" + fieldName + "'][.='1995-12-31T11:59:59Z']",
                "//result/doc[3]/date[@name='" + fieldName + "'][.='1995-12-31T12:59:59Z']",
                "//result/doc[4]/date[@name='" + fieldName + "'][.='1995-12-31T13:59:59Z']");

        assertQ(req("q",
                fieldName + ":" + String.format(Locale.ROOT, "{%s+0HOURS TO %s+3HOURS]", baseDate, baseDate), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T11:59:59Z']",
                "//result/doc[2]/date[@name='" + fieldName + "'][.='1995-12-31T12:59:59Z']",
                "//result/doc[3]/date[@name='" + fieldName + "'][.='1995-12-31T13:59:59Z']");

        assertQ(req("q",
                fieldName + ":" + String.format(Locale.ROOT, "[%s+0HOURS TO %s+3HOURS}", baseDate, baseDate), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T10:59:59Z']",
                "//result/doc[2]/date[@name='" + fieldName + "'][.='1995-12-31T11:59:59Z']",
                "//result/doc[3]/date[@name='" + fieldName + "'][.='1995-12-31T12:59:59Z']");

        assertQ(req("q",
                fieldName + ":" + String.format(Locale.ROOT, "{%s+0HOURS TO %s+3HOURS}", baseDate, baseDate), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='2']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T11:59:59Z']",
                "//result/doc[2]/date[@name='" + fieldName + "'][.='1995-12-31T12:59:59Z']");

        assertQ(req("q", fieldName + ":" + String.format(Locale.ROOT, "{%s+0HOURS TO *}", baseDate), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='9']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T11:59:59Z']");

        assertQ(req("q", fieldName + ":" + String.format(Locale.ROOT, "{* TO %s+3HOURS}", baseDate), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T10:59:59Z']");

        assertQ(req("q", fieldName + ":" + String.format(Locale.ROOT, "[* TO %s+3HOURS}", baseDate), "fl",
                "id, " + fieldName, "sort", "id asc"), "//*[@numFound='3']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T10:59:59Z']");

        assertQ(req("q", fieldName + ":[* TO *}", "fl", "id, " + fieldName, "sort", "id asc"),
                "//*[@numFound='10']", "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T10:59:59Z']",
                "//result/doc[10]/date[@name='" + fieldName + "'][.='1995-12-31T19:59:59Z']");

        assertQ(req("q",
                fieldName + ":" + String.format(Locale.ROOT, "[%s+0HOURS TO %s+1HOURS]", baseDate, baseDate)
                        + " OR " + fieldName + ":"
                        + String.format(Locale.ROOT, "[%s+8HOURS TO %s+9HOURS]", baseDate, baseDate),
                "fl", "id, " + fieldName, "sort", "id asc"), "//*[@numFound='4']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T10:59:59Z']",
                "//result/doc[2]/date[@name='" + fieldName + "'][.='1995-12-31T11:59:59Z']",
                "//result/doc[3]/date[@name='" + fieldName + "'][.='1995-12-31T18:59:59Z']",
                "//result/doc[4]/date[@name='" + fieldName + "'][.='1995-12-31T19:59:59Z']");

        assertQ(req("q",
                fieldName + ":" + String.format(Locale.ROOT, "[%s+0HOURS TO %s+1HOURS]", baseDate, baseDate)
                        + " AND " + fieldName + ":"
                        + String.format(Locale.ROOT, "[%s+1HOURS TO %s+2HOURS]", baseDate, baseDate),
                "fl", "id, " + fieldName), "//*[@numFound='1']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T11:59:59Z']");

        assertQ(req("q",
                fieldName + ":" + String.format(Locale.ROOT, "[%s+0HOURS TO %s+1HOURS]", baseDate, baseDate)
                        + " AND NOT " + fieldName + ":"
                        + String.format(Locale.ROOT, "[%s+1HOURS TO %s+2HOURS]", baseDate, baseDate),
                "fl", "id, " + fieldName), "//*[@numFound='1']",
                "//result/doc[1]/date[@name='" + fieldName + "'][.='1995-12-31T10:59:59Z']");

        clearIndex();
        assertU(commit());
    }

    private void doTestDatePointFieldRangeFacet(String docValuesField, String nonDocValuesField) throws Exception {
        String baseDate = "1995-01-10T10:59:59Z";
        for (int i = 0; i < 10; i++) {
            String date = String.format(Locale.ROOT, "%s+%dDAYS", baseDate, i);
            assertU(adoc("id", String.valueOf(i), docValuesField, date, nonDocValuesField, date));
        }
        assertU(commit());
        assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
        assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
        assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z-10DAYS", "facet.range.end", "1995-01-10T10:59:59Z+10DAYS", "facet.range.gap",
                "+2DAYS"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-12T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-14T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-16T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-18T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-08T10:59:59Z'][.='0']");

        assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z-10DAYS", "facet.range.end", "1995-01-10T10:59:59Z+10DAYS", "facet.range.gap",
                "+2DAYS", "facet.range.method", "dv"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-12T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-14T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-16T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-18T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-08T10:59:59Z'][.='0']");

        assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
        assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
        // Range Faceting with method = filter should work
        assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z-10DAYS", "facet.range.end", "1995-01-10T10:59:59Z+10DAYS", "facet.range.gap",
                "+2DAYS", "facet.range.method", "filter"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-12T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-14T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-16T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-18T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-08T10:59:59Z'][.='0']");

        // this should actually use filter method instead of dv
        assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z-10DAYS", "facet.range.end", "1995-01-10T10:59:59Z+10DAYS", "facet.range.gap",
                "+2DAYS", "facet.range.method", "dv"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-12T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-14T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-16T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-18T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-08T10:59:59Z'][.='0']");
    }

    private void doTestDatePointFieldMultiValuedRangeFacet(String docValuesField, String nonDocValuesField)
            throws Exception {
        String baseDate = "1995-01-10T10:59:59Z";
        for (int i = 0; i < 10; i++) {
            String date1 = String.format(Locale.ROOT, "%s+%dDAYS", baseDate, i);
            String date2 = String.format(Locale.ROOT, "%s+%dDAYS", baseDate, i + 10);
            assertU(adoc("id", String.valueOf(i), docValuesField, date1, docValuesField, date2, nonDocValuesField,
                    date1, nonDocValuesField, date2));
        }
        assertU(commit());
        assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
        assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
        assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z-10DAYS", "facet.range.end", "1995-01-10T10:59:59Z+20DAYS", "facet.range.gap",
                "+2DAYS"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-12T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-14T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-16T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-18T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-20T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-22T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-24T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-26T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-28T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1994-12-31T10:59:59Z'][.='0']");

        assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z-10DAYS", "facet.range.end", "1995-01-10T10:59:59Z+20DAYS", "facet.range.gap",
                "+2DAYS", "facet.range.method", "dv"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-12T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-14T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-16T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-18T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-20T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-22T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-24T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-26T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-28T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1994-12-31T10:59:59Z'][.='0']");

        assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", docValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z", "facet.range.end", "1995-01-10T10:59:59Z+20DAYS", "facet.range.gap",
                "+100DAYS"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='10']");

        assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
        assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
        // Range Faceting with method = filter should work
        assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z-10DAYS", "facet.range.end", "1995-01-10T10:59:59Z+20DAYS", "facet.range.gap",
                "+2DAYS", "facet.range.method", "filter"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-12T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-14T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-16T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-18T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-20T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-22T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-24T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-26T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-28T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1994-12-31T10:59:59Z'][.='0']");

        // this should actually use filter method instead of dv
        assertQ(req("q", "*:*", "fl", "id", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start",
                "1995-01-10T10:59:59Z-10DAYS", "facet.range.end", "1995-01-10T10:59:59Z+20DAYS", "facet.range.gap",
                "+2DAYS", "facet.range.method", "dv"), "//*[@numFound='10']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-10T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-12T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-14T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-16T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-18T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-20T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-22T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-24T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-26T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1995-01-28T10:59:59Z'][.='2']",
                "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
                        + "']/lst[@name='counts']/int[@name='1994-12-31T10:59:59Z'][.='0']");
    }

    private void doTestDatePointFunctionQuery(String field, String nonDvFieldName) throws Exception {
        final String baseDate = "1995-01-10T10:59:10Z";

        for (int i = 9; i >= 0; i--) {
            String date = String.format(Locale.ROOT, "%s+%dSECONDS", baseDate, i + 1);
            assertU(adoc("id", String.valueOf(i), field, date));
        }
        assertU(commit());
        assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof DatePointField);
        assertQ(req("q", "*:*", "fl", "id, " + field, "sort", "product(-1,ms(" + field + ")) asc"),
                "//*[@numFound='10']", "//result/doc[1]/date[@name='" + field + "'][.='1995-01-10T10:59:20Z']",
                "//result/doc[2]/date[@name='" + field + "'][.='1995-01-10T10:59:19Z']",
                "//result/doc[3]/date[@name='" + field + "'][.='1995-01-10T10:59:18Z']",
                "//result/doc[10]/date[@name='" + field + "'][.='1995-01-10T10:59:11Z']");

        assertQ(req("q", "*:*", "fl", "id, " + field + ", ms(" + field + "," + baseDate + ")", "sort", "id asc"),
                "//*[@numFound='10']",
                "//result/doc[1]/float[@name='ms(" + field + "," + baseDate + ")'][.='1000.0']",
                "//result/doc[2]/float[@name='ms(" + field + "," + baseDate + ")'][.='2000.0']",
                "//result/doc[3]/float[@name='ms(" + field + "," + baseDate + ")'][.='3000.0']",
                "//result/doc[10]/float[@name='ms(" + field + "," + baseDate + ")'][.='10000.0']");

        assertQ(req("q", "*:*", "fl", "id, " + field + ", field(" + field + ")", "sort", "id asc"),
                "//*[@numFound='10']",
                "//result/doc[1]/date[@name='field(" + field + ")'][.='1995-01-10T10:59:11Z']",
                "//result/doc[2]/date[@name='field(" + field + ")'][.='1995-01-10T10:59:12Z']",
                "//result/doc[3]/date[@name='field(" + field + ")'][.='1995-01-10T10:59:13Z']",
                "//result/doc[10]/date[@name='field(" + field + ")'][.='1995-01-10T10:59:20Z']");

    }

    private void testDatePointStats(String field, String dvField, String[] dates) {
        for (int i = 0; i < dates.length; i++) {
            assertU(adoc("id", String.valueOf(i), dvField, dates[i], field, dates[i]));
        }
        assertU(adoc("id", String.valueOf(dates.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 + "']/date[@name='min'][.='"
                        + dates[0] + "']",
                "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField + "']/date[@name='max'][.='"
                        + dates[dates.length - 1] + "']",
                "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField
                        + "']/long[@name='count'][.='" + dates.length + "']",
                "//lst[@name='stats']/lst[@name='stats_fields']/lst[@name='" + dvField
                        + "']/long[@name='missing'][.='1']");

        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);
    }

    private void testDatePointFieldsAtomicUpdates(String field, String type) throws Exception {
        String date = "1995-01-10T10:59:10Z";
        assertU(adoc(sdoc("id", "1", field, date)));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.='" + date + "']");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", date + "+2DAYS"))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/" + type + "[@name='" + field + "'][.='1995-01-12T10:59:10Z']");
    }

    private void testMultiValuedDatePointFieldsAtomicUpdates(String field, String type) throws Exception {
        String date1 = "1995-01-10T10:59:10Z";
        String date2 = "1995-01-11T10:59:10Z";
        String date3 = "1995-01-12T10:59:10Z";
        assertU(adoc(sdoc("id", "1", field, date1)));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + date1 + "']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=1");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("add", date2))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + date1 + "']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + date2 + "']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=2");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("remove", date1))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + date2 + "']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=1");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", ImmutableList.of(date1, date2, date3)))));
        assertU(commit());

        assertQ(req("q", "id:1"), "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + date1 + "']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + date2 + "']",
                "//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + date3 + "']",
                "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=3");

        assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("removeregex", ".*"))));
        assertU(commit());

        assertQ(req("q", "id:1"), "count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=0");

    }

    private void doTestInternals(String field, String[] values) throws IOException {
        assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
        for (int i = 0; i < 10; i++) {
            assertU(adoc("id", String.valueOf(i), field, values[i]));
        }
        assertU(commit());
        IndexReader ir;
        RefCounted<SolrIndexSearcher> ref = null;
        SchemaField sf = h.getCore().getLatestSchema().getField(field);
        boolean ignoredField = !(sf.indexed() || sf.stored() || sf.hasDocValues());
        try {
            ref = h.getCore().getSearcher();
            SolrIndexSearcher searcher = ref.get();
            ir = searcher.getIndexReader();
            // our own SlowCompositeReader to check DocValues on disk w/o the UninvertingReader added by SolrIndexSearcher
            final LeafReader leafReaderForCheckingDVs = SlowCompositeReaderWrapper.wrap(searcher.getRawReader());

            if (sf.indexed()) {
                assertEquals("Field " + field + " should have point values", 10, PointValues.size(ir, field));
            } else {
                assertEquals("Field " + field + " should have no point values", 0, PointValues.size(ir, field));
            }
            if (ignoredField) {
                assertTrue("Field " + field + " should not have docValues",
                        DocValues.getSortedNumeric(leafReaderForCheckingDVs, field)
                                .nextDoc() == DocIdSetIterator.NO_MORE_DOCS);
                assertTrue("Field " + field + " should not have docValues", DocValues
                        .getNumeric(leafReaderForCheckingDVs, field).nextDoc() == DocIdSetIterator.NO_MORE_DOCS);
                assertTrue("Field " + field + " should not have docValues", DocValues
                        .getSorted(leafReaderForCheckingDVs, field).nextDoc() == DocIdSetIterator.NO_MORE_DOCS);
                assertTrue("Field " + field + " should not have docValues", DocValues
                        .getBinary(leafReaderForCheckingDVs, field).nextDoc() == DocIdSetIterator.NO_MORE_DOCS);
            } else {
                if (sf.hasDocValues()) {
                    if (sf.multiValued()) {
                        assertFalse("Field " + field + " should have docValues",
                                DocValues.getSortedNumeric(leafReaderForCheckingDVs, field)
                                        .nextDoc() == DocIdSetIterator.NO_MORE_DOCS);
                    } else {
                        assertFalse("Field " + field + " should have docValues",
                                DocValues.getNumeric(leafReaderForCheckingDVs, field)
                                        .nextDoc() == DocIdSetIterator.NO_MORE_DOCS);
                    }
                } else {
                    expectThrows(IllegalStateException.class,
                            () -> DocValues.getSortedNumeric(leafReaderForCheckingDVs, field));
                    expectThrows(IllegalStateException.class,
                            () -> DocValues.getNumeric(leafReaderForCheckingDVs, field));
                }
                expectThrows(IllegalStateException.class,
                        () -> DocValues.getSorted(leafReaderForCheckingDVs, field));
                expectThrows(IllegalStateException.class,
                        () -> DocValues.getBinary(leafReaderForCheckingDVs, field));
            }
            for (LeafReaderContext leave : ir.leaves()) {
                LeafReader reader = leave.reader();
                for (int i = 0; i < reader.numDocs(); i++) {
                    Document doc = reader.document(i);
                    if (sf.stored()) {
                        assertNotNull("Field " + field + " not found. Doc: " + doc, doc.get(field));
                    } else {
                        assertNull(doc.get(field));
                    }
                }
            }
        } finally {
            ref.decref();
        }
        clearIndex();
        assertU(commit());
    }

    public void testNonReturnable() throws Exception {

        doTestReturnNonStored("foo_p_i_ni_ns", false, "42");
        doTestReturnNonStored("foo_p_i_ni_dv_ns", true, "42");
        doTestReturnNonStored("foo_p_i_ni_ns_mv", false, "42", "666");
        doTestReturnNonStored("foo_p_i_ni_dv_ns_mv", true, "42", "666");

        doTestReturnNonStored("foo_p_l_ni_ns", false, "3333333333");
        doTestReturnNonStored("foo_p_l_ni_dv_ns", true, "3333333333");
        doTestReturnNonStored("foo_p_l_ni_ns_mv", false, "3333333333", "-4444444444");
        doTestReturnNonStored("foo_p_l_ni_dv_ns_mv", true, "3333333333", "-4444444444");

        doTestReturnNonStored("foo_p_f_ni_ns", false, "42.3");
        doTestReturnNonStored("foo_p_f_ni_dv_ns", true, "42.3");
        doTestReturnNonStored("foo_p_f_ni_ns_mv", false, "42.3", "-66.6");
        doTestReturnNonStored("foo_p_f_ni_dv_ns_mv", true, "42.3", "-66.6");

        doTestReturnNonStored("foo_p_d_ni_ns", false, "42.3");
        doTestReturnNonStored("foo_p_d_ni_dv_ns", true, "42.3");
        doTestReturnNonStored("foo_p_d_ni_ns_mv", false, "42.3", "-66.6");
        doTestReturnNonStored("foo_p_d_ni_dv_ns_mv", true, "42.3", "-66.6");

        doTestReturnNonStored("foo_p_dt_ni_ns", false, "1995-12-31T23:59:59Z");
        doTestReturnNonStored("foo_p_dt_ni_dv_ns", true, "1995-12-31T23:59:59Z");
        doTestReturnNonStored("foo_p_dt_ni_ns_mv", false, "1995-12-31T23:59:59Z", "2000-12-31T23:59:59Z+3DAYS");
        doTestReturnNonStored("foo_p_dt_ni_dv_ns_mv", true, "1995-12-31T23:59:59Z", "2000-12-31T23:59:59Z+3DAYS");
    }

    public void doTestReturnNonStored(final String fieldName, boolean shouldReturnFieldIfRequested,
            final String... values) throws Exception {
        final String RETURN_FIELD = "count(//doc/*[@name='" + fieldName + "'])=10";
        final String DONT_RETURN_FIELD = "count(//doc/*[@name='" + fieldName + "'])=0";
        assertFalse(h.getCore().getLatestSchema().getField(fieldName).stored());
        for (int i = 0; i < 10; i++) {
            SolrInputDocument doc = sdoc("id", String.valueOf(i));
            for (String value : values) {
                doc.addField(fieldName, value);
            }
            assertU(adoc(doc));
        }
        assertU(commit());
        assertQ(req("q", "*:*", "rows", "100", "fl", "id," + fieldName), "//*[@numFound='10']", "count(//doc)=10", // exactly 10 docs in response
                (shouldReturnFieldIfRequested ? RETURN_FIELD : DONT_RETURN_FIELD)); // no field in any doc other then 'id'

        assertQ(req("q", "*:*", "rows", "100", "fl", "*"), "//*[@numFound='10']", "count(//doc)=10", // exactly 10 docs in response
                DONT_RETURN_FIELD); // no field in any doc other then 'id'

        assertQ(req("q", "*:*", "rows", "100"), "//*[@numFound='10']", "count(//doc)=10", // exactly 10 docs in response
                DONT_RETURN_FIELD); // no field in any doc other then 'id'
        clearIndex();
        assertU(commit());
    }

    public void testWhiteboxCreateFields() throws Exception {
        String[] typeNames = new String[] { "i", "l", "f", "d", "dt" };
        String[] suffixes = new String[] { "", "_dv", "_mv", "_mv_dv", "_ni", "_ni_dv", "_ni_dv_ns", "_ni_dv_ns_mv",
                "_ni_mv", "_ni_mv_dv", "_ni_ns", "_ni_ns_mv", "_dv_ns", "_ni_ns_dv", "_dv_ns_mv" };
        Class<?>[] expectedClasses = new Class[] { IntPoint.class, LongPoint.class, FloatPoint.class,
                DoublePoint.class, LongPoint.class };

        Date dateToTest = new Date();
        Object[][] values = new Object[][] { { 42, "42" }, { 42, "42" }, { 42.123, "42.123" },
                { 12345.6789, "12345.6789" }, { dateToTest,
                        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT).format(dateToTest), "NOW" } // "NOW" won't be equal to the other dates
        };

        Set<String> typesTested = new HashSet<>();
        for (int i = 0; i < typeNames.length; i++) {
            for (String suffix : suffixes) {
                doWhiteboxCreateFields("whitebox_p_" + typeNames[i] + suffix, expectedClasses[i], values[i]);
                typesTested.add("*_p_" + typeNames[i] + suffix);
            }
        }
        Set<String> typesToTest = new HashSet<>();
        for (DynamicField dynField : h.getCore().getLatestSchema().getDynamicFields()) {
            if (dynField.getPrototype().getType() instanceof PointField) {
                typesToTest.add(dynField.getRegex());
            }
        }
        assertEquals("Missing types in the test", typesTested, typesToTest);
    }

    /** 
     * Calls {@link #callAndCheckCreateFields} on each of the specified values.
     * This is a convinience method for testing the same fieldname with multiple inputs.
     *
     * @see #callAndCheckCreateFields
     */
    private void doWhiteboxCreateFields(final String fieldName, final Class<?> pointType, final Object... values)
            throws Exception {

        for (Object value : values) {
            // ideally we should require that all input values be diff forms of the same logical value
            // (ie '"42"' vs 'new Integer(42)') and assert that each produces an equivilent list of IndexableField objects
            // but that doesn't seem to work -- appears not all IndexableField classes override Object.equals?
            final List<IndexableField> result = callAndCheckCreateFields(fieldName, pointType, value);
            assertNotNull(value + " => null", result);
        }
    }

    /** 
     * Calls {@link SchemaField#createFields} on the specified value for the specified field name, and asserts 
     * that the results match the SchemaField propeties, with an additional check that the <code>pointType</code> 
     * is included if and only if the SchemaField is "indexed" 
     */
    private List<IndexableField> callAndCheckCreateFields(final String fieldName, final Class<?> pointType,
            final Object value) throws Exception {
        final SchemaField sf = h.getCore().getLatestSchema().getField(fieldName);
        final List<IndexableField> results = sf.createFields(value);
        final Set<IndexableField> resultSet = new LinkedHashSet<>(results);
        assertEquals("duplicates found in results? " + results.toString(), results.size(), resultSet.size());

        final Set<Class<?>> resultClasses = new HashSet<>();
        for (IndexableField f : results) {
            resultClasses.add(f.getClass());

            if (!sf.hasDocValues()) {
                assertFalse(f.toString(),
                        (f instanceof NumericDocValuesField) || (f instanceof SortedNumericDocValuesField));
            }
        }
        assertEquals(fieldName + " stored? Result Fields: " + Arrays.toString(results.toArray()), sf.stored(),
                resultClasses.contains(StoredField.class));
        assertEquals(fieldName + " indexed? Result Fields: " + Arrays.toString(results.toArray()), sf.indexed(),
                resultClasses.contains(pointType));
        if (sf.multiValued()) {
            assertEquals(fieldName + " docvalues? Result Fields: " + Arrays.toString(results.toArray()),
                    sf.hasDocValues(), resultClasses.contains(SortedNumericDocValuesField.class));
        } else {
            assertEquals(fieldName + " docvalues? Result Fields: " + Arrays.toString(results.toArray()),
                    sf.hasDocValues(), resultClasses.contains(NumericDocValuesField.class));
        }

        return results;
    }

}