blob: b185a13291eea7e9fef9ded19c0e48937d0686de [file] [log] [blame]
/*
* 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.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import com.google.common.collect.ImmutableMap;
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.DirectoryReader;
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.SolrQueryParser;
import org.apache.solr.util.DateMathParser;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
/** Tests for PointField functionality */
public class TestPointFields extends SolrTestCaseJ4 {
// long overflow can occur in some date calculations if gaps are too large, so we limit to a million years BC & AD.
private static final long MIN_DATE_EPOCH_MILLIS = LocalDateTime.parse("-1000000-01-01T00:00:00").toInstant(ZoneOffset.ofHours(0)).toEpochMilli();
private static final long MAX_DATE_EPOCH_MILLIS = LocalDateTime.parse("+1000000-01-01T00:00:00").toInstant(ZoneOffset.ofHours(0)).toEpochMilli();
private static final String[] FIELD_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",
"_smf", "_dv_smf", "_mv_smf", "_mv_dv_smf", "_ni_dv_smf", "_ni_mv_dv_smf",
"_sml", "_dv_sml", "_mv_sml", "_mv_dv_sml", "_ni_dv_sml", "_ni_mv_dv_sml"
};
@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 {
int numValues = 10 * RANDOM_MULTIPLIER;
String[] ints = toStringArray(getRandomInts(numValues, false));
doTestPointFieldReturn("number_p_i", "int", ints);
doTestPointFieldReturn("number_p_i_dv_ns", "int", ints);
doTestPointFieldReturn("number_p_i_ni", "int", ints);
}
@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", toStringArray(getRandomInts(1, false)));
doTestPointFieldNonSearchableRangeQuery("number_p_i_ni_ns", toStringArray(getRandomInts(1, false)));
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldNonSearchableRangeQuery("number_p_i_ni_ns_mv", toStringArray(getRandomInts(numValues, false)));
}
@Test
public void testIntPointFieldSortAndFunction() throws Exception {
final SortedSet<String> regexToTest = dynFieldRegexesForType(IntPointField.class);
final List<String> sequential = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
final List<Integer> randomInts = getRandomInts(10, false);
final List<Integer> randomIntsMissing = getRandomInts(10, true);
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));
String field = r.replace("*", "number");
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomInts);
doTestIntPointFunctionQuery(field);
}
for (String r : Arrays.asList("*_p_i_smf", "*_p_i_dv_smf", "*_p_i_ni_dv_smf",
"*_p_i_sml", "*_p_i_dv_sml", "*_p_i_ni_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomIntsMissing);
doTestIntPointFunctionQuery(field);
}
// no docvalues
for (String r : Arrays.asList("*_p_i_ni", "*_p_i_ni_ns")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", toStringArray(getRandomInts(1, false)));
doTestPointFieldFunctionQueryError(field, "w/o docValues", toStringArray(getRandomInts(1, false)));
}
// multivalued, no docvalues
for (String r : Arrays.asList("*_p_i_mv", "*_p_i_ni_mv", "*_p_i_ni_ns_mv",
"*_p_i_mv_smf", "*_p_i_mv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", toStringArray(getRandomInts(1, false)));
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldSortError(field, "w/o docValues", toStringArray(getRandomInts(numValues, false)));
doTestPointFieldFunctionQueryError(field, "multivalued", toStringArray(getRandomInts(1, false)));
doTestPointFieldFunctionQueryError(field, "multivalued", toStringArray(getRandomInts(numValues, false)));
}
// multivalued, w/ docValues
for (String r : Arrays.asList("*_p_i_ni_mv_dv", "*_p_i_ni_dv_ns_mv",
"*_p_i_dv_ns_mv", "*_p_i_mv_dv",
"*_p_i_mv_dv_smf", "*_p_i_ni_mv_dv_smf",
"*_p_i_mv_dv_sml", "*_p_i_ni_mv_dv_sml"
)) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
// NOTE: only testing one value per doc here, but TestMinMaxOnMultiValuedField
// covers this in more depth
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomInts);
// value source (w/o field(...,min|max)) usuage should still error...
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldFunctionQueryError(field, "multivalued", toStringArray(getRandomInts(1, false)));
doTestPointFieldFunctionQueryError(field, "multivalued", toStringArray(getRandomInts(numValues, false)));
}
assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);
}
@Test
public void testIntPointFieldFacetField() throws Exception {
doTestPointFieldFacetField("number_p_i", "number_p_i_dv", getSequentialStringArrayWithInts(10));
clearIndex();
assertU(commit());
doTestPointFieldFacetField("number_p_i", "number_p_i_dv", toStringArray(getRandomInts(10, false)));
}
@Test
public void testIntPointFieldRangeFacet() throws Exception {
String docValuesField = "number_p_i_dv";
String nonDocValuesField = "number_p_i";
int numValues = 10 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Integer> values;
List<Integer> sortedValues;
int max;
do {
values = getRandomInts(numValues, false);
sortedValues = values.stream().sorted().collect(Collectors.toList());
} while ((max = sortedValues.get(sortedValues.size() - 1)) >= Integer.MAX_VALUE - numValues); // leave room for rounding
int min = sortedValues.get(0);
int gap = (int)(((long)(max + numValues) - (long)min) / (long)numBuckets);
int[] bucketCount = new int[numBuckets];
int bucketNum = 0;
int minBucketVal = min;
for (Integer value : sortedValues) {
while (((long)value - (long)minBucketVal) >= (long)gap) {
++bucketNum;
minBucketVal += gap;
}
++bucketCount[bucketNum];
}
for (int i = 0 ; i < numValues ; i++) {
assertU(adoc("id", String.valueOf(i), docValuesField, String.valueOf(values.get(i)), nonDocValuesField, String.valueOf(values.get(i))));
}
assertU(commit());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
String[] testStrings = new String[numBuckets + 1];
testStrings[numBuckets] = "//*[@numFound='" + numValues + "']";
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + bucketCount[i] + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", String.valueOf(min),
"facet.range.end", String.valueOf(max), "facet.range.gap", String.valueOf(gap)),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", String.valueOf(min),
"facet.range.end", String.valueOf(max), "facet.range.gap", String.valueOf(gap), "facet.range.method", "dv"),
testStrings);
assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + bucketCount[i] + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", String.valueOf(min),
"facet.range.end", String.valueOf(max), "facet.range.gap", String.valueOf(gap), "facet.range.method", "filter"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", String.valueOf(min),
"facet.range.end", String.valueOf(max), "facet.range.gap", String.valueOf(gap), "facet.range.method", "dv"),
testStrings);
}
@Test
public void testIntPointStats() throws Exception {
int numValues = 10 * RANDOM_MULTIPLIER;
// don't produce numbers with exponents, since XPath comparison operators can't handle them
List<Integer> values = getRandomInts(numValues, false, 9999999);
// System.err.println(Arrays.toString(values.toArray(new Integer[values.size()])));
List<Integer> sortedValues = values.stream().sorted().collect(Collectors.toList());
double min = (double)sortedValues.get(0);
double max = (double)sortedValues.get(sortedValues.size() - 1);
String[] valArray = toStringArray(values);
doTestPointStats("number_p_i", "number_p_i_dv", valArray, min, max, numValues, 1, 0D);
doTestPointStats("number_p_i", "number_p_i_mv_dv", valArray, min, max, numValues, 1, 0D);
}
@Test
public void testIntPointFieldMultiValuedExactQuery() throws Exception {
String[] ints = toStringArray(getRandomInts(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_i_mv", ints);
doTestPointFieldMultiValuedExactQuery("number_p_i_ni_mv_dv", ints);
}
@Test
public void testIntPointFieldMultiValuedNonSearchableExactQuery() throws Exception {
String[] ints = toStringArray(getRandomInts(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_i_ni_mv", ints, false);
doTestPointFieldMultiValuedExactQuery("number_p_i_ni_ns_mv", ints, false);
}
@Test
public void testIntPointFieldMultiValuedReturn() throws Exception {
String[] ints = toStringArray(getRandomInts(20, false));
doTestPointFieldMultiValuedReturn("number_p_i_mv", "int", ints);
doTestPointFieldMultiValuedReturn("number_p_i_ni_mv_dv", "int", ints);
doTestPointFieldMultiValuedReturn("number_p_i_dv_ns_mv", "int", ints);
}
@Test
public void testIntPointFieldMultiValuedRangeQuery() throws Exception {
String[] ints = toStringArray(getRandomInts(20, false).stream().sorted().collect(Collectors.toList()));
doTestPointFieldMultiValuedRangeQuery("number_p_i_mv", "int", ints);
doTestPointFieldMultiValuedRangeQuery("number_p_i_ni_mv_dv", "int", ints);
doTestPointFieldMultiValuedRangeQuery("number_p_i_mv_dv", "int", ints);
}
@Test
public void testIntPointFieldNotIndexed() throws Exception {
String[] ints = toStringArray(getRandomInts(10, false));
doTestFieldNotIndexed("number_p_i_ni", ints);
doTestFieldNotIndexed("number_p_i_ni_mv", ints);
}
//TODO MV SORT?
@Test
public void testIntPointFieldMultiValuedFacetField() throws Exception {
doTestPointFieldMultiValuedFacetField("number_p_i_mv", "number_p_i_mv_dv", getSequentialStringArrayWithInts(20));
String[] randomSortedInts = toStringArray(getRandomInts(20, false).stream().sorted().collect(Collectors.toList()));
doTestPointFieldMultiValuedFacetField("number_p_i_mv", "number_p_i_mv_dv", randomSortedInts);
}
@Test
public void testIntPointFieldMultiValuedRangeFacet() throws Exception {
String docValuesField = "number_p_i_mv_dv";
String nonDocValuesField = "number_p_i_mv";
int numValues = 20 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Integer> values;
List<PosVal<Integer>> sortedValues;
int max;
do {
values = getRandomInts(numValues, false);
sortedValues = toAscendingPosVals(values, true);
} while ((max = sortedValues.get(sortedValues.size() - 1).val) >= Integer.MAX_VALUE - numValues); // leave room for rounding
int min = sortedValues.get(0).val;
int gap = (int)(((long)(max + numValues) - (long)min) / (long)numBuckets);
List<Set<Integer>> docIdBucket = new ArrayList<>(numBuckets);
for (int i = 0 ; i < numBuckets ; ++i) {
docIdBucket.add(new HashSet<>());
}
int bucketNum = 0;
int minBucketVal = min;
for (PosVal<Integer> value : sortedValues) {
while ((long)value.val - (long)minBucketVal >= gap) {
++bucketNum;
minBucketVal += gap;
}
docIdBucket.get(bucketNum).add(value.pos / 2); // each doc gets two consecutive values
}
for (int i = 0 ; i < numValues ; i += 2) {
assertU(adoc("id", String.valueOf(i / 2),
docValuesField, String.valueOf(values.get(i)),
docValuesField, String.valueOf(values.get(i + 1)),
nonDocValuesField, String.valueOf(values.get(i)),
nonDocValuesField, String.valueOf(values.get(i + 1))));
}
assertU(commit());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
String[] testStrings = new String[numBuckets + 1];
minBucketVal = min;
testStrings[numBuckets] = "//*[@numFound='" + (numValues / 2) + "']";
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + docIdBucket.get(i).size() + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", String.valueOf(min), "facet.range.end", String.valueOf(max),
"facet.range.gap", String.valueOf(gap), "indent", "on"),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", String.valueOf(min), "facet.range.end", String.valueOf(max),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "dv", "indent", "on"),
testStrings);
assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + docIdBucket.get(i).size() + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", String.valueOf(min), "facet.range.end", String.valueOf(max),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "filter", "indent", "on"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", String.valueOf(min), "facet.range.end", String.valueOf(max),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "dv", "indent", "on"),
testStrings);
}
@Test
public void testIntPointMultiValuedFunctionQuery() throws Exception {
doTestPointMultiValuedFunctionQuery("number_p_i_mv", "number_p_i_mv_dv", "int", getSequentialStringArrayWithInts(20));
doTestPointMultiValuedFunctionQuery("number_p_i_mv", "number_p_i_mv_dv", "int",
toStringArray(getRandomInts(20, false).stream().sorted().collect(Collectors.toList())));
}
@Test
public void testIntPointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
doTestIntPointFieldsAtomicUpdates("number_p_i");
doTestIntPointFieldsAtomicUpdates("number_p_i_dv");
doTestIntPointFieldsAtomicUpdates("number_p_i_dv_ns");
}
@Test
public void testMultiValuedIntPointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
String[] ints = toStringArray(getRandomInts(3, false));
doTestMultiValuedPointFieldsAtomicUpdates("number_p_i_mv", "int", ints);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_i_ni_mv_dv", "int", ints);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_i_dv_ns_mv", "int", ints);
}
private <T> String[] toStringArray(List<T> list) {
return list.stream().map(String::valueOf).collect(Collectors.toList()).toArray(new String[list.size()]);
}
private class PosVal <T extends Comparable<T>> {
int pos;
T val;
PosVal(int pos, T val) {
this.pos = pos;
this.val = val;
}
public String toString() {
return "(" + pos + ": " + val.toString() + ")";
}
}
/** Primary sort by value, with nulls either first or last as specified, and then secondary sort by position. */
private <T extends Comparable<T>>
Comparator<PosVal<T>> getPosValComparator(final boolean ascending, final boolean nullsFirst) {
return (o1, o2) -> {
if (o1.val == null) {
if (o2.val == null) {
return ascending ? Integer.compare(o1.pos, o2.pos) : Integer.compare(o2.pos, o1.pos);
} else {
return nullsFirst ? -1 : 1;
}
} else if (o2.val == null) {
return nullsFirst ? 1 : -1;
} else {
return ascending ? o1.val.compareTo(o2.val) : o2.val.compareTo(o1.val);
}
};
}
/**
* Primary ascending sort by value, with missing values (represented as null) either first or last as specified,
* and then secondary ascending sort by position.
*/
private <T extends Comparable<T>> String[] toAscendingStringArray(List<T> list, boolean missingFirst) {
return toStringArray(toAscendingPosVals(list, missingFirst).stream().map(pv -> pv.val).collect(Collectors.toList()));
}
/**
* Primary ascending sort by value, with missing values (represented as null) either first or last as specified,
* and then secondary ascending sort by position.
*
* @return a list of the (originally) positioned values sorted as described above.
*/
private <T extends Comparable<T>> List<PosVal<T>> toAscendingPosVals(List<T> list, boolean missingFirst) {
List<PosVal<T>> posVals = IntStream.range(0, list.size())
.mapToObj(i -> new PosVal<>(i, list.get(i))).collect(Collectors.toList());
posVals.sort(getPosValComparator(true, missingFirst));
return posVals;
}
/**
* Primary descending sort by value, with missing values (represented as null) either first or last as specified,
* and then secondary descending sort by position.
*
* @return a list of the (originally) positioned values sorted as described above.
*/
private <T extends Comparable<T>> List<PosVal<T>> toDescendingPosVals(List<T> list, boolean missingFirst) {
List<PosVal<T>> posVals = IntStream.range(0, list.size())
.mapToObj(i -> new PosVal<>(i, list.get(i))).collect(Collectors.toList());
posVals.sort(getPosValComparator(false, missingFirst));
return posVals;
}
@Test
public void testIntPointSetQuery() throws Exception {
doTestSetQueries("number_p_i", toStringArray(getRandomInts(20, false)), false);
doTestSetQueries("number_p_i_mv", toStringArray(getRandomInts(20, false)), true);
doTestSetQueries("number_p_i_ni_dv", toStringArray(getRandomInts(20, false)), false);
}
// DoublePointField
@Test
public void testDoublePointFieldExactQuery() throws Exception {
doTestFloatPointFieldExactQuery("number_p_d", true);
doTestFloatPointFieldExactQuery("number_p_d_mv", true);
doTestFloatPointFieldExactQuery("number_p_d_dv", true);
doTestFloatPointFieldExactQuery("number_p_d_mv_dv", true);
doTestFloatPointFieldExactQuery("number_p_d_ni_dv", true);
doTestFloatPointFieldExactQuery("number_p_d_ni_ns_dv", true);
doTestFloatPointFieldExactQuery("number_p_d_ni_dv_ns", true);
doTestFloatPointFieldExactQuery("number_p_d_ni_mv_dv", true);
}
@Test
public void testDoublePointFieldNonSearchableExactQuery() throws Exception {
doTestFloatPointFieldExactQuery("number_p_d_ni", false, true);
doTestFloatPointFieldExactQuery("number_p_d_ni_ns", false, true);
}
@Test
public void testDoublePointFieldReturn() throws Exception {
int numValues = 10 * RANDOM_MULTIPLIER;
String[] doubles = toStringArray(getRandomDoubles(numValues, false));
doTestPointFieldReturn("number_p_d", "double", doubles);
doTestPointFieldReturn("number_p_d_dv_ns", "double", doubles);
}
@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", toStringArray(getRandomDoubles(1, false)));
doTestPointFieldNonSearchableRangeQuery("number_p_d_ni_ns", toStringArray(getRandomDoubles(1, false)));
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldNonSearchableRangeQuery("number_p_d_ni_ns_mv", toStringArray(getRandomDoubles(numValues, false)));
}
@Test
public void testDoublePointFieldSortAndFunction() throws Exception {
final SortedSet<String> regexToTest = dynFieldRegexesForType(DoublePointField.class);
final List<String> sequential = Arrays.asList("0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0");
List<Double> randomDoubles = getRandomDoubles(10, false);
List<Double> randomDoublesMissing = getRandomDoubles(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));
String field = r.replace("*", "number");
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomDoubles);
doTestDoublePointFunctionQuery(field);
}
for (String r : Arrays.asList("*_p_d_smf", "*_p_d_dv_smf", "*_p_d_ni_dv_smf",
"*_p_d_sml", "*_p_d_dv_sml", "*_p_d_ni_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomDoublesMissing);
doTestDoublePointFunctionQuery(field);
}
for (String r : Arrays.asList("*_p_d_ni", "*_p_d_ni_ns")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", "42.34");
doTestPointFieldFunctionQueryError(field, "w/o docValues", "42.34");
}
// multivalued, no docvalues
for (String r : Arrays.asList("*_p_d_mv", "*_p_d_ni_mv", "*_p_d_ni_ns_mv",
"*_p_d_mv_smf", "*_p_d_mv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", "42.34");
doTestPointFieldSortError(field, "w/o docValues", "42.34", "66.6");
doTestPointFieldFunctionQueryError(field, "multivalued", "42.34");
doTestPointFieldFunctionQueryError(field, "multivalued", "42.34", "66.6");
}
// multivalued, w/ docValues
for (String r : Arrays.asList("*_p_d_ni_mv_dv", "*_p_d_ni_dv_ns_mv",
"*_p_d_dv_ns_mv", "*_p_d_mv_dv",
"*_p_d_mv_dv_smf", "*_p_d_ni_mv_dv_smf",
"*_p_d_mv_dv_sml", "*_p_d_ni_mv_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
// NOTE: only testing one value per doc here, but TestMinMaxOnMultiValuedField
// covers this in more depth
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomDoubles);
// value source (w/o field(...,min|max)) usuage should still error...
doTestPointFieldFunctionQueryError(field, "multivalued", "42.34");
doTestPointFieldFunctionQueryError(field, "multivalued", "42.34", "66.6");
}
assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);
}
@Test
public void testDoublePointFieldFacetField() throws Exception {
doTestPointFieldFacetField("number_p_d", "number_p_d_dv", getSequentialStringArrayWithDoubles(10));
clearIndex();
assertU(commit());
doTestPointFieldFacetField("number_p_d", "number_p_d_dv", toStringArray(getRandomDoubles(10, false)));
}
@Test
public void testDoublePointFieldRangeFacet() throws Exception {
String docValuesField = "number_p_d_dv";
String nonDocValuesField = "number_p_d";
int numValues = 10 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Double> values, sortedValues;
double min, max, gap, buffer;
do {
values = getRandomDoubles(numValues, false);
sortedValues = values.stream().sorted().collect(Collectors.toList());
min = sortedValues.get(0);
max = sortedValues.get(sortedValues.size() - 1);
buffer = BigDecimal.valueOf(max).subtract(BigDecimal.valueOf(min))
.divide(BigDecimal.valueOf(numValues / 2), RoundingMode.HALF_UP).doubleValue();
gap = BigDecimal.valueOf(max).subtract(BigDecimal.valueOf(min)).add(BigDecimal.valueOf(buffer * 2.0D))
.divide(BigDecimal.valueOf(numBuckets), RoundingMode.HALF_UP).doubleValue();
} while (max >= Double.MAX_VALUE - buffer || min <= -Double.MAX_VALUE + buffer);
// System.err.println("min: " + min + " max: " + max + " gap: " + gap + " buffer: " + buffer);
int[] bucketCount = new int[numBuckets];
int bucketNum = 0;
double minBucketVal = min - buffer;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
for (double value : sortedValues) {
// System.err.println("value: " + value);
while (value - minBucketVal >= gap) {
++bucketNum;
minBucketVal += gap;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
}
++bucketCount[bucketNum];
}
for (int i = 0 ; i < numValues ; i++) {
assertU(adoc("id", String.valueOf(i),
docValuesField, String.valueOf(values.get(i)), nonDocValuesField, String.valueOf(values.get(i))));
}
assertU(commit());
String[] testStrings = new String[numBuckets + 1];
testStrings[numBuckets] = "//*[@numFound='" + numValues + "']";
minBucketVal = min - buffer;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + bucketCount[i] + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", String.valueOf(min - buffer),
"facet.range.end", String.valueOf(max + buffer), "facet.range.gap", String.valueOf(gap)),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", String.valueOf(min - buffer),
"facet.range.end", String.valueOf(max + buffer), "facet.range.gap", String.valueOf(gap), "facet.range.method", "dv"),
testStrings);
minBucketVal = min - buffer;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + bucketCount[i] + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", String.valueOf(min - buffer),
"facet.range.end", String.valueOf(max + buffer), "facet.range.gap", String.valueOf(gap), "facet.range.method", "filter"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", String.valueOf(min - buffer),
"facet.range.end", String.valueOf(max + buffer), "facet.range.gap", String.valueOf(gap), "facet.range.method", "dv"),
testStrings);
}
@Test
public void testDoublePointStats() throws Exception {
int numValues = 10 * RANDOM_MULTIPLIER;
// don't produce numbers with exponents, since XPath comparison operators can't handle them: 7 digits of precision
List<Float> values = getRandomInts(numValues, false, 9999999).stream()
.map(v -> (float)((double)v * Math.pow(10D, -1 * random().nextInt(8)))).collect(Collectors.toList());
// System.err.println(Arrays.toString(values.toArray(new Float[values.size()])));
List<Float> sortedValues = values.stream().sorted().collect(Collectors.toList());
double min = (double)sortedValues.get(0);
double max = (double)sortedValues.get(sortedValues.size() - 1);
String[] valArray = toStringArray(values);
doTestPointStats("number_p_d", "number_p_d_dv", valArray, min, max, numValues, 1, 1E-7D);
doTestPointStats("number_p_d", "number_p_d_mv_dv", valArray, min, max, numValues, 1, 1E-7D);
}
@Test
public void testDoublePointFieldMultiValuedExactQuery() throws Exception {
String[] doubles = toStringArray(getRandomDoubles(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_d_mv", doubles);
doTestPointFieldMultiValuedExactQuery("number_p_d_ni_mv_dv", doubles);
}
@Test
public void testDoublePointFieldMultiValuedNonSearchableExactQuery() throws Exception {
String[] doubles = toStringArray(getRandomDoubles(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_d_ni_mv", doubles, false);
doTestPointFieldMultiValuedExactQuery("number_p_d_ni_ns_mv", doubles, false);
}
@Test
public void testDoublePointFieldMultiValuedReturn() throws Exception {
String[] doubles = toStringArray(getRandomDoubles(20, false));
doTestPointFieldMultiValuedReturn("number_p_d_mv", "double", doubles);
doTestPointFieldMultiValuedReturn("number_p_d_ni_mv_dv", "double", doubles);
doTestPointFieldMultiValuedReturn("number_p_d_dv_ns_mv", "double", doubles);
}
@Test
public void testDoublePointFieldMultiValuedRangeQuery() throws Exception {
String[] doubles = toStringArray(getRandomDoubles(20, false).stream().sorted().collect(Collectors.toList()));
doTestPointFieldMultiValuedRangeQuery("number_p_d_mv", "double", doubles);
doTestPointFieldMultiValuedRangeQuery("number_p_d_ni_mv_dv", "double", doubles);
doTestPointFieldMultiValuedRangeQuery("number_p_d_mv_dv", "double", doubles);
}
@Test
public void testDoublePointFieldMultiValuedFacetField() throws Exception {
doTestPointFieldMultiValuedFacetField("number_p_d_mv", "number_p_d_mv_dv", getSequentialStringArrayWithDoubles(20));
doTestPointFieldMultiValuedFacetField("number_p_d_mv", "number_p_d_mv_dv", toStringArray(getRandomDoubles(20, false)));
}
@Test
public void testDoublePointFieldMultiValuedRangeFacet() throws Exception {
String docValuesField = "number_p_d_mv_dv";
SchemaField dvSchemaField = h.getCore().getLatestSchema().getField(docValuesField);
assertTrue(dvSchemaField.multiValued());
assertTrue(dvSchemaField.hasDocValues());
assertTrue(dvSchemaField.getType() instanceof PointField);
String nonDocValuesField = "number_p_d_mv";
SchemaField nonDvSchemaField = h.getCore().getLatestSchema().getField(nonDocValuesField);
assertTrue(nonDvSchemaField.multiValued());
assertFalse(nonDvSchemaField.hasDocValues());
assertTrue(nonDvSchemaField.getType() instanceof PointField);
int numValues = 20 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Double> values;
List<PosVal<Double>> sortedValues;
double min, max, gap, buffer;
do {
values = getRandomDoubles(numValues, false);
sortedValues = toAscendingPosVals(values, true);
min = sortedValues.get(0).val;
max = sortedValues.get(sortedValues.size() - 1).val;
buffer = BigDecimal.valueOf(max).subtract(BigDecimal.valueOf(min))
.divide(BigDecimal.valueOf(numValues / 2), RoundingMode.HALF_UP).doubleValue();
gap = BigDecimal.valueOf(max).subtract(BigDecimal.valueOf(min)).add(BigDecimal.valueOf(buffer * 2.0D))
.divide(BigDecimal.valueOf(numBuckets), RoundingMode.HALF_UP).doubleValue();
} while (max >= Double.MAX_VALUE - buffer || min <= -Double.MAX_VALUE + buffer);
// System.err.println("min: " + min + " max: " + max + " gap: " + gap + " buffer: " + buffer);
List<Set<Integer>> docIdBucket = new ArrayList<>(numBuckets);
for (int i = 0 ; i < numBuckets ; ++i) {
docIdBucket.add(new HashSet<>());
}
int bucketNum = 0;
double minBucketVal = min - buffer;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
for (PosVal<Double> value : sortedValues) {
// System.err.println("value.val: " + value.val);
while (value.val - minBucketVal >= gap) {
++bucketNum;
minBucketVal += gap;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
}
docIdBucket.get(bucketNum).add(value.pos / 2); // each doc gets two consecutive values
}
for (int i = 0 ; i < numValues ; i += 2) {
assertU(adoc("id", String.valueOf(i / 2),
docValuesField, String.valueOf(values.get(i)),
docValuesField, String.valueOf(values.get(i + 1)),
nonDocValuesField, String.valueOf(values.get(i)),
nonDocValuesField, String.valueOf(values.get(i + 1))));
}
assertU(commit());
String[] testStrings = new String[numBuckets + 1];
testStrings[numBuckets] = "//*[@numFound='" + (numValues / 2) + "']";
minBucketVal = min - buffer;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + docIdBucket.get(i).size() + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", String.valueOf(min - buffer), "facet.range.end", String.valueOf(max + buffer),
"facet.range.gap", String.valueOf(gap), "indent", "on"),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", String.valueOf(min - buffer), "facet.range.end", String.valueOf(max + buffer),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "dv", "indent", "on"),
testStrings);
minBucketVal = min - buffer;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + docIdBucket.get(i).size() + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", String.valueOf(min - buffer), "facet.range.end", String.valueOf(max + buffer),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "filter", "indent", "on"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", String.valueOf(min - buffer), "facet.range.end", String.valueOf(max + buffer),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "dv", "indent", "on"),
testStrings);
}
@Test
public void testDoublePointMultiValuedFunctionQuery() throws Exception {
doTestPointMultiValuedFunctionQuery("number_p_d_mv", "number_p_d_mv_dv", "double", getSequentialStringArrayWithDoubles(20));
doTestPointMultiValuedFunctionQuery("number_p_d_mv", "number_p_d_mv_dv", "double", toAscendingStringArray(getRandomFloats(20, false), true));
}
@Test
public void testDoublePointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
doTestDoublePointFieldsAtomicUpdates("number_p_d");
doTestDoublePointFieldsAtomicUpdates("number_p_d_dv");
doTestDoublePointFieldsAtomicUpdates("number_p_d_dv_ns");
}
@Test
public void testMultiValuedDoublePointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
String[] doubles = toStringArray(getRandomDoubles(3, false));
doTestMultiValuedPointFieldsAtomicUpdates("number_p_d_mv", "double", doubles);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_d_ni_mv_dv", "double", doubles);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_d_dv_ns_mv", "double", doubles);
}
@Test
public void testDoublePointFieldNotIndexed() throws Exception {
String[] doubles = toStringArray(getRandomDoubles(10, false));
doTestFieldNotIndexed("number_p_d_ni", doubles);
doTestFieldNotIndexed("number_p_d_ni_mv", doubles);
}
private void doTestFloatPointFieldsAtomicUpdates(String field) throws Exception {
float number1 = getRandomFloats(1, false).get(0);
float number2;
double inc1;
for ( ; ; ) {
number2 = getRandomFloats(1, false).get(0);
inc1 = (double)number2 - (double)number1;
if (Math.abs(inc1) < (double)Float.MAX_VALUE) {
number2 = number1 + (float)inc1;
break;
}
}
assertU(adoc(sdoc("id", "1", field, String.valueOf(number1))));
assertU(commit());
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", (float)inc1))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/float[@name='" + field + "'][.='" + number2 + "']");
float number3 = getRandomFloats(1, false).get(0);
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", number3))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/float[@name='" + field + "'][.='" + number3 + "']");
}
private void doTestDoublePointFieldsAtomicUpdates(String field) throws Exception {
double number1 = getRandomDoubles(1, false).get(0);
double number2;
BigDecimal inc1;
for ( ; ; ) {
number2 = getRandomDoubles(1, false).get(0);
inc1 = BigDecimal.valueOf(number2).subtract(BigDecimal.valueOf(number1));
if (inc1.abs().compareTo(BigDecimal.valueOf(Double.MAX_VALUE)) <= 0) {
number2 = number1 + inc1.doubleValue();
break;
}
}
assertU(adoc(sdoc("id", "1", field, String.valueOf(number1))));
assertU(commit());
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", inc1.doubleValue()))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/double[@name='" + field + "'][.='" + number2 + "']");
double number3 = getRandomDoubles(1, false).get(0);
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", number3))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/double[@name='" + field + "'][.='" + number3 + "']");
}
@Test
public void testDoublePointSetQuery() throws Exception {
doTestSetQueries("number_p_d", toStringArray(getRandomDoubles(20, false)), false);
doTestSetQueries("number_p_d_mv", toStringArray(getRandomDoubles(20, false)), true);
doTestSetQueries("number_p_d_ni_dv", toStringArray(getRandomDoubles(20, false)), false);
}
// Float
@Test
public void testFloatPointFieldExactQuery() throws Exception {
doTestFloatPointFieldExactQuery("number_p_f", false);
doTestFloatPointFieldExactQuery("number_p_f_mv", false);
doTestFloatPointFieldExactQuery("number_p_f_dv", false);
doTestFloatPointFieldExactQuery("number_p_f_mv_dv", false);
doTestFloatPointFieldExactQuery("number_p_f_ni_dv", false);
doTestFloatPointFieldExactQuery("number_p_f_ni_ns_dv", false);
doTestFloatPointFieldExactQuery("number_p_f_ni_dv_ns", false);
doTestFloatPointFieldExactQuery("number_p_f_ni_mv_dv", false);
}
@Test
public void testFloatPointFieldNonSearchableExactQuery() throws Exception {
doTestFloatPointFieldExactQuery("number_p_f_ni", false, false);
doTestFloatPointFieldExactQuery("number_p_f_ni_ns", false, false);
}
@Test
public void testFloatPointFieldReturn() throws Exception {
int numValues = 10 * RANDOM_MULTIPLIER;
String[] floats = toStringArray(getRandomFloats(numValues, false));
doTestPointFieldReturn("number_p_f", "float", floats);
doTestPointFieldReturn("number_p_f_dv_ns", "float", floats);
}
@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", toStringArray(getRandomFloats(1, false)));
doTestPointFieldNonSearchableRangeQuery("number_p_f_ni_ns", toStringArray(getRandomFloats(1, false)));
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldNonSearchableRangeQuery("number_p_f_ni_ns_mv", toStringArray(getRandomFloats(numValues, false)));
}
@Test
public void testFloatPointFieldSortAndFunction() throws Exception {
final SortedSet<String> regexToTest = dynFieldRegexesForType(FloatPointField.class);
final List<String> sequential = Arrays.asList("0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0");
final List<Float> randomFloats = getRandomFloats(10, false);
final List<Float> randomFloatsMissing = getRandomFloats(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));
String field = r.replace("*", "number");
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomFloats);
doTestFloatPointFunctionQuery(field);
}
for (String r : Arrays.asList("*_p_f_smf", "*_p_f_dv_smf", "*_p_f_ni_dv_smf",
"*_p_f_sml", "*_p_f_dv_sml", "*_p_f_ni_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomFloatsMissing);
doTestFloatPointFunctionQuery(field);
}
for (String r : Arrays.asList("*_p_f_ni", "*_p_f_ni_ns")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", "42.34");
doTestPointFieldFunctionQueryError(field, "w/o docValues", "42.34");
}
// multivalued, no docvalues
for (String r : Arrays.asList("*_p_f_mv", "*_p_f_ni_mv", "*_p_f_ni_ns_mv",
"*_p_f_mv_smf", "*_p_f_mv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", "42.34");
doTestPointFieldSortError(field, "w/o docValues", "42.34", "66.6");
doTestPointFieldFunctionQueryError(field, "multivalued", "42.34");
doTestPointFieldFunctionQueryError(field, "multivalued", "42.34", "66.6");
}
// multivalued, w/ docValues
for (String r : Arrays.asList("*_p_f_ni_mv_dv", "*_p_f_ni_dv_ns_mv",
"*_p_f_dv_ns_mv", "*_p_f_mv_dv",
"*_p_f_mv_dv_smf", "*_p_f_ni_mv_dv_smf",
"*_p_f_mv_dv_sml", "*_p_f_ni_mv_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
// NOTE: only testing one value per doc here, but TestMinMaxOnMultiValuedField
// covers this in more depth
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomFloats);
// value source (w/o field(...,min|max)) usuage should still error...
doTestPointFieldFunctionQueryError(field, "multivalued", "42.34");
doTestPointFieldFunctionQueryError(field, "multivalued", "42.34", "66.6");
}
assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);
}
@Test
public void testFloatPointFieldFacetField() throws Exception {
doTestPointFieldFacetField("number_p_f", "number_p_f_dv", getSequentialStringArrayWithDoubles(10));
clearIndex();
assertU(commit());
doTestPointFieldFacetField("number_p_f", "number_p_f_dv", toStringArray(getRandomFloats(10, false)));
}
@Test
public void testFloatPointFieldRangeFacet() throws Exception {
String docValuesField = "number_p_f_dv";
String nonDocValuesField = "number_p_f";
int numValues = 10 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Float> values, sortedValues;
float min, max, gap, buffer;
do {
values = getRandomFloats(numValues, false);
sortedValues = values.stream().sorted().collect(Collectors.toList());
min = sortedValues.get(0);
max = sortedValues.get(sortedValues.size() - 1);
buffer = (float)(((double)max - (double)min) / (double)numValues / 2.0D);
gap = (float)(((double)max + (double)buffer - (double)min + (double)buffer) / (double)numBuckets);
} while (max >= Float.MAX_VALUE - buffer || min <= -Float.MAX_VALUE + buffer);
// System.err.println("min: " + min + " max: " + max + " gap: " + gap + " buffer: " + buffer);
int[] bucketCount = new int[numBuckets];
int bucketNum = 0;
float minBucketVal = min - buffer;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
for (float value : sortedValues) {
// System.err.println("value: " + value);
while (value - minBucketVal >= gap) {
++bucketNum;
minBucketVal += gap;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
}
++bucketCount[bucketNum];
}
for (int i = 0 ; i < numValues ; i++) {
assertU(adoc("id", String.valueOf(i),
docValuesField, String.valueOf(values.get(i)), nonDocValuesField, String.valueOf(values.get(i))));
}
assertU(commit());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
String[] testStrings = new String[numBuckets + 1];
testStrings[numBuckets] = "//*[@numFound='" + numValues + "']";
minBucketVal = min - buffer;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + bucketCount[i] + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", String.valueOf(min - buffer),
"facet.range.end", String.valueOf(max + buffer), "facet.range.gap", String.valueOf(gap)),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", String.valueOf(min - buffer),
"facet.range.end", String.valueOf(max + buffer), "facet.range.gap", String.valueOf(gap), "facet.range.method", "dv"),
testStrings);
assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
minBucketVal = min - buffer;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + bucketCount[i] + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", String.valueOf(min - buffer),
"facet.range.end", String.valueOf(max + buffer), "facet.range.gap", String.valueOf(gap), "facet.range.method", "filter"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", String.valueOf(min - buffer),
"facet.range.end", String.valueOf(max + buffer), "facet.range.gap", String.valueOf(gap), "facet.range.method", "dv"),
testStrings);
}
@Test
public void testFloatPointStats() throws Exception {
int numValues = 10 * RANDOM_MULTIPLIER;
// don't produce numbers with exponents, since XPath comparison operators can't handle them: 7 digits of precision
List<Float> values = getRandomInts(numValues, false, 9999999).stream()
.map(v -> (float)((double)v * Math.pow(10D, -1 * random().nextInt(8)))).collect(Collectors.toList());
// System.err.println(Arrays.toString(values.toArray(new Float[values.size()])));
List<Float> sortedValues = values.stream().sorted().collect(Collectors.toList());
double min = (double)sortedValues.get(0);
double max = (double)sortedValues.get(sortedValues.size() - 1);
String[] valArray = toStringArray(values);
doTestPointStats("number_p_f", "number_p_f_dv", valArray, min, max, numValues, 1, 1E-7D);
doTestPointStats("number_p_f", "number_p_f_mv_dv", valArray, min, max, numValues, 1, 1E-7D);
}
@Test
public void testFloatPointFieldMultiValuedExactQuery() throws Exception {
String[] floats = toStringArray(getRandomFloats(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_f_mv", floats);
doTestPointFieldMultiValuedExactQuery("number_p_f_ni_mv_dv", floats);
}
@Test
public void testFloatPointFieldMultiValuedNonSearchableExactQuery() throws Exception {
String[] floats = toStringArray(getRandomFloats(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_f_ni_mv", floats, false);
doTestPointFieldMultiValuedExactQuery("number_p_f_ni_ns_mv", floats, false);
}
@Test
public void testFloatPointFieldMultiValuedReturn() throws Exception {
String[] floats = toStringArray(getRandomFloats(20, false));
doTestPointFieldMultiValuedReturn("number_p_f_mv", "float", floats);
doTestPointFieldMultiValuedReturn("number_p_f_ni_mv_dv", "float", floats);
doTestPointFieldMultiValuedReturn("number_p_f_dv_ns_mv", "float", floats);
}
@Test
public void testFloatPointFieldMultiValuedRangeQuery() throws Exception {
String[] floats = toStringArray(getRandomFloats(20, false).stream().sorted().collect(Collectors.toList()));
doTestPointFieldMultiValuedRangeQuery("number_p_f_mv", "float", floats);
doTestPointFieldMultiValuedRangeQuery("number_p_f_ni_mv_dv", "float", floats);
doTestPointFieldMultiValuedRangeQuery("number_p_f_mv_dv", "float", floats);
}
@Test
public void testFloatPointFieldMultiValuedRangeFacet() throws Exception {
String docValuesField = "number_p_f_mv_dv";
SchemaField dvSchemaField = h.getCore().getLatestSchema().getField(docValuesField);
assertTrue(dvSchemaField.multiValued());
assertTrue(dvSchemaField.hasDocValues());
assertTrue(dvSchemaField.getType() instanceof PointField);
String nonDocValuesField = "number_p_f_mv";
SchemaField nonDvSchemaField = h.getCore().getLatestSchema().getField(nonDocValuesField);
assertTrue(nonDvSchemaField.multiValued());
assertFalse(nonDvSchemaField.hasDocValues());
assertTrue(nonDvSchemaField.getType() instanceof PointField);
int numValues = 20 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Float> values;
List<PosVal<Float>> sortedValues;
float min, max, gap, buffer;
do {
values = getRandomFloats(numValues, false);
sortedValues = toAscendingPosVals(values, true);
min = sortedValues.get(0).val;
max = sortedValues.get(sortedValues.size() - 1).val;
buffer = (float)(((double)max - (double)min) / (double)numValues / 2.0D);
gap = (float)(((double)max + (double)buffer - (double)min + (double)buffer) / (double)numBuckets);
} while (max >= Float.MAX_VALUE - buffer || min <= -Float.MAX_VALUE + buffer);
// System.err.println("min: " + min + " max: " + max + " gap: " + gap + " buffer: " + buffer);
List<Set<Integer>> docIdBucket = new ArrayList<>(numBuckets);
for (int i = 0 ; i < numBuckets ; ++i) {
docIdBucket.add(new HashSet<>());
}
int bucketNum = 0;
float minBucketVal = min - buffer;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
for (PosVal<Float> value : sortedValues) {
// System.err.println("value.val: " + value.val);
while (value.val - minBucketVal >= gap) {
++bucketNum;
minBucketVal += gap;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
}
docIdBucket.get(bucketNum).add(value.pos / 2); // each doc gets two consecutive values
}
for (int i = 0 ; i < numValues ; i += 2) {
assertU(adoc("id", String.valueOf(i / 2),
docValuesField, String.valueOf(values.get(i)),
docValuesField, String.valueOf(values.get(i + 1)),
nonDocValuesField, String.valueOf(values.get(i)),
nonDocValuesField, String.valueOf(values.get(i + 1))));
}
assertU(commit());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
String[] testStrings = new String[numBuckets + 1];
minBucketVal = min - buffer;
testStrings[numBuckets] = "//*[@numFound='" + (numValues / 2) + "']";
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + docIdBucket.get(i).size() + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", String.valueOf(min - buffer), "facet.range.end", String.valueOf(max + buffer),
"facet.range.gap", String.valueOf(gap), "indent", "on"),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", String.valueOf(min - buffer), "facet.range.end", String.valueOf(max + buffer),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "dv", "indent", "on"),
testStrings);
assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
minBucketVal = min - buffer;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + docIdBucket.get(i).size() + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", String.valueOf(min - buffer), "facet.range.end", String.valueOf(max + buffer),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "filter", "indent", "on"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", String.valueOf(min - buffer), "facet.range.end", String.valueOf(max + buffer),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "dv", "indent", "on"),
testStrings);
}
@Test
public void testFloatPointFieldMultiValuedFacetField() throws Exception {
doTestPointFieldMultiValuedFacetField("number_p_f_mv", "number_p_f_mv_dv", getSequentialStringArrayWithDoubles(20));
doTestPointFieldMultiValuedFacetField("number_p_f_mv", "number_p_f_mv_dv", toStringArray(getRandomFloats(20, false)));
}
@Test
public void testFloatPointMultiValuedFunctionQuery() throws Exception {
doTestPointMultiValuedFunctionQuery("number_p_f_mv", "number_p_f_mv_dv", "float", getSequentialStringArrayWithDoubles(20));
doTestPointMultiValuedFunctionQuery("number_p_f_mv", "number_p_f_mv_dv", "float", toAscendingStringArray(getRandomFloats(20, false), true));
}
@Test
public void testFloatPointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
doTestFloatPointFieldsAtomicUpdates("number_p_f");
doTestFloatPointFieldsAtomicUpdates("number_p_f_dv");
doTestFloatPointFieldsAtomicUpdates("number_p_f_dv_ns");
}
@Test
public void testMultiValuedFloatPointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
String[] floats = toStringArray(getRandomFloats(3, false));
doTestMultiValuedPointFieldsAtomicUpdates("number_p_f_mv", "float", floats);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_f_ni_mv_dv", "float", floats);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_f_dv_ns_mv", "float", floats);
}
@Test
public void testFloatPointSetQuery() throws Exception {
doTestSetQueries("number_p_f", toStringArray(getRandomFloats(20, false)), false);
doTestSetQueries("number_p_f_mv", toStringArray(getRandomFloats(20, false)), true);
doTestSetQueries("number_p_f_ni_dv", toStringArray(getRandomFloats(20, false)), false);
}
@Test
public void testFloatPointFieldNotIndexed() throws Exception {
String[] floats = toStringArray(getRandomFloats(10, false));
doTestFieldNotIndexed("number_p_f_ni", floats);
doTestFieldNotIndexed("number_p_f_ni_mv", floats);
}
// 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 {
int numValues = 10 * RANDOM_MULTIPLIER;
String[] longs = toStringArray(getRandomLongs(numValues, false));
doTestPointFieldReturn("number_p_l", "long", longs);
doTestPointFieldReturn("number_p_l_dv_ns", "long", longs);
}
@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", toStringArray(getRandomLongs(1, false)));
doTestPointFieldNonSearchableRangeQuery("number_p_l_ni_ns", toStringArray(getRandomLongs(1, false)));
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldNonSearchableRangeQuery("number_p_l_ni_ns_mv", toStringArray(getRandomLongs(numValues, false)));
}
@Test
public void testLongPointFieldSortAndFunction() throws Exception {
final SortedSet<String> regexToTest = dynFieldRegexesForType(LongPointField.class);
final List<Long> vals = Arrays.asList((long)Integer.MIN_VALUE,
1L, 2L, 3L, 4L, 5L, 6L, 7L,
(long)Integer.MAX_VALUE, Long.MAX_VALUE);
final List<Long> randomLongs = getRandomLongs(10, false);
final List<Long> randomLongsMissing = getRandomLongs(10, true);
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));
String field = r.replace("*", "number");
doTestPointFieldSort(field, vals);
doTestPointFieldSort(field, randomLongs);
doTestLongPointFunctionQuery(field);
}
for (String r : Arrays.asList("*_p_l_smf", "*_p_l_dv_smf", "*_p_l_ni_dv_smf",
"*_p_l_sml", "*_p_l_dv_sml", "*_p_l_ni_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSort(field, vals);
doTestPointFieldSort(field, randomLongsMissing);
doTestLongPointFunctionQuery(field);
}
// no docvalues
for (String r : Arrays.asList("*_p_l_ni", "*_p_l_ni_ns")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", toStringArray(getRandomLongs(1, false)));
doTestPointFieldFunctionQueryError(field, "w/o docValues", toStringArray(getRandomLongs(1, false)));
}
// multivalued, no docvalues
for (String r : Arrays.asList("*_p_l_mv", "*_p_l_ni_mv", "*_p_l_ni_ns_mv",
"*_p_l_mv_smf", "*_p_l_mv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", toStringArray(getRandomLongs(1, false)));
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldSortError(field, "w/o docValues", toStringArray(getRandomLongs(numValues, false)));
doTestPointFieldFunctionQueryError(field, "multivalued", toStringArray(getRandomLongs(1, false)));
doTestPointFieldFunctionQueryError(field, "multivalued", toStringArray(getRandomLongs(numValues, false)));
}
// multivalued, w/ docValues
for (String r : Arrays.asList("*_p_l_ni_mv_dv", "*_p_l_ni_dv_ns_mv",
"*_p_l_dv_ns_mv", "*_p_l_mv_dv",
"*_p_l_mv_dv_smf", "*_p_l_ni_mv_dv_smf",
"*_p_l_mv_dv_sml", "*_p_l_ni_mv_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
// NOTE: only testing one value per doc here, but TestMinMaxOnMultiValuedField
// covers this in more depth
doTestPointFieldSort(field, vals);
doTestPointFieldSort(field, randomLongs);
// value source (w/o field(...,min|max)) usuage should still error...
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldFunctionQueryError(field, "multivalued", toStringArray(getRandomLongs(1, false)));
doTestPointFieldFunctionQueryError(field, "multivalued", toStringArray(getRandomLongs(numValues, false)));
}
assertEquals("Missing types in the test", Collections.<String>emptySet(), regexToTest);
}
@Test
public void testLongPointFieldFacetField() throws Exception {
doTestPointFieldFacetField("number_p_l", "number_p_l_dv", getSequentialStringArrayWithInts(10));
clearIndex();
assertU(commit());
doTestPointFieldFacetField("number_p_l", "number_p_l_dv", toStringArray(getRandomLongs(10, false)));
}
@Test
public void testLongPointFieldRangeFacet() throws Exception {
String docValuesField = "number_p_l_dv";
String nonDocValuesField = "number_p_l";
int numValues = 10 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Long> values;
List<Long> sortedValues;
long max;
do {
values = getRandomLongs(numValues, false);
sortedValues = values.stream().sorted().collect(Collectors.toList());
} while ((max = sortedValues.get(sortedValues.size() - 1)) >= Long.MAX_VALUE - numValues); // leave room for rounding
long min = sortedValues.get(0);
BigInteger bigIntGap = BigInteger.valueOf(max + numValues).subtract(BigInteger.valueOf(min))
.divide(BigInteger.valueOf(numBuckets));
long gap = bigIntGap.longValueExact();
int[] bucketCount = new int[numBuckets];
int bucketNum = 0;
long minBucketVal = min;
// System.err.println("min:" + min + " max: " + max + " gap: " + gap);
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
for (Long value : sortedValues) {
// System.err.println("value: " + value);
while (BigInteger.valueOf(value).subtract(BigInteger.valueOf(minBucketVal)).compareTo(bigIntGap) > 0) {
++bucketNum;
minBucketVal += gap;
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + minBucketVal);
}
++bucketCount[bucketNum];
}
for (int i = 0 ; i < numValues ; i++) {
assertU(adoc("id", String.valueOf(i), docValuesField, String.valueOf(values.get(i)), nonDocValuesField, String.valueOf(values.get(i))));
}
assertU(commit());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
String[] testStrings = new String[numBuckets + 1];
testStrings[numBuckets] = "//*[@numFound='" + numValues + "']";
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + bucketCount[i] + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", String.valueOf(min),
"facet.range.end", String.valueOf(max), "facet.range.gap", String.valueOf(gap)),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField, "facet.range.start", String.valueOf(min),
"facet.range.end", String.valueOf(max), "facet.range.gap", String.valueOf(gap), "facet.range.method", "dv"),
testStrings);
assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + bucketCount[i] + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", String.valueOf(min),
"facet.range.end", String.valueOf(max), "facet.range.gap", String.valueOf(gap), "facet.range.method", "filter"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField, "facet.range.start", String.valueOf(min),
"facet.range.end", String.valueOf(max), "facet.range.gap", String.valueOf(gap), "facet.range.method", "dv"),
testStrings);
}
@Test
public void testLongPointStats() throws Exception {
int numValues = 10 * RANDOM_MULTIPLIER;
// don't produce numbers with exponents, since XPath comparison operators can't handle them
List<Long> values = getRandomLongs(numValues, false, 9999999L);
List<Long> sortedValues = values.stream().sorted().collect(Collectors.toList());
double min = (double)sortedValues.get(0);
double max = (double)sortedValues.get(sortedValues.size() - 1);
String[] valArray = toStringArray(values);
doTestPointStats("number_p_l", "number_p_l_dv", valArray, min, max, numValues, 1, 0D);
doTestPointStats("number_p_l", "number_p_l_mv_dv", valArray, min, max, numValues, 1, 0D);
}
@Test
public void testLongPointFieldMultiValuedExactQuery() throws Exception {
String[] ints = toStringArray(getRandomInts(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_l_mv", ints);
doTestPointFieldMultiValuedExactQuery("number_p_l_ni_mv_dv", ints);
}
@Test
public void testLongPointFieldMultiValuedNonSearchableExactQuery() throws Exception {
String[] longs = toStringArray(getRandomLongs(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_l_ni_mv", longs, false);
doTestPointFieldMultiValuedExactQuery("number_p_l_ni_ns_mv", longs, false);
}
@Test
public void testLongPointFieldMultiValuedReturn() throws Exception {
String[] longs = toStringArray(getRandomLongs(20, false));
doTestPointFieldMultiValuedReturn("number_p_l_mv", "long", longs);
doTestPointFieldMultiValuedReturn("number_p_l_ni_mv_dv", "long", longs);
doTestPointFieldMultiValuedReturn("number_p_l_dv_ns_mv", "long", longs);
}
@Test
public void testLongPointFieldMultiValuedRangeQuery() throws Exception {
String[] longs = toStringArray(getRandomLongs(20, false).stream().sorted().collect(Collectors.toList()));
doTestPointFieldMultiValuedRangeQuery("number_p_l_mv", "long", longs);
doTestPointFieldMultiValuedRangeQuery("number_p_l_ni_mv_dv", "long", longs);
doTestPointFieldMultiValuedRangeQuery("number_p_l_mv_dv", "long", longs);
}
@Test
public void testLongPointFieldMultiValuedFacetField() throws Exception {
doTestPointFieldMultiValuedFacetField("number_p_l_mv", "number_p_l_mv_dv", getSequentialStringArrayWithInts(20));
doTestPointFieldMultiValuedFacetField("number_p_l_mv", "number_p_l_mv_dv", toStringArray(getRandomLongs(20, false)));
}
@Test
public void testLongPointFieldMultiValuedRangeFacet() throws Exception {
String docValuesField = "number_p_l_mv_dv";
String nonDocValuesField = "number_p_l_mv";
int numValues = 20 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Long> values;
List<PosVal<Long>> sortedValues;
long max;
do {
values = getRandomLongs(numValues, false);
sortedValues = toAscendingPosVals(values, true);
} while ((max = sortedValues.get(sortedValues.size() - 1).val) >= Long.MAX_VALUE - numValues); // leave room for rounding
long min = sortedValues.get(0).val;
long gap = BigInteger.valueOf(max + numValues).subtract(BigInteger.valueOf(min))
.divide(BigInteger.valueOf(numBuckets)).longValueExact();
List<Set<Integer>> docIdBucket = new ArrayList<>(numBuckets);
for (int i = 0 ; i < numBuckets ; ++i) {
docIdBucket.add(new HashSet<>());
}
int bucketNum = 0;
long minBucketVal = min;
for (PosVal<Long> value : sortedValues) {
while (value.val - minBucketVal >= gap) {
++bucketNum;
minBucketVal += gap;
}
docIdBucket.get(bucketNum).add(value.pos / 2); // each doc gets two consecutive values
}
for (int i = 0 ; i < numValues ; i += 2) {
assertU(adoc("id", String.valueOf(i / 2),
docValuesField, String.valueOf(values.get(i)),
docValuesField, String.valueOf(values.get(i + 1)),
nonDocValuesField, String.valueOf(values.get(i)),
nonDocValuesField, String.valueOf(values.get(i + 1))));
}
assertU(commit());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
String[] testStrings = new String[numBuckets + 1];
testStrings[numBuckets] = "//*[@numFound='" + (numValues / 2) + "']";
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + docIdBucket.get(i).size() + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", String.valueOf(min), "facet.range.end", String.valueOf(max),
"facet.range.gap", String.valueOf(gap), "indent", "on"),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", String.valueOf(min), "facet.range.end", String.valueOf(max),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "dv", "indent", "on"),
testStrings);
assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal += gap, ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + minBucketVal + "'][.='" + docIdBucket.get(i).size() + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", String.valueOf(min), "facet.range.end", String.valueOf(max),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "filter", "indent", "on"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", String.valueOf(min), "facet.range.end", String.valueOf(max),
"facet.range.gap", String.valueOf(gap), "facet.range.method", "dv", "indent", "on"),
testStrings);
}
@Test
public void testLongPointMultiValuedFunctionQuery() throws Exception {
doTestPointMultiValuedFunctionQuery("number_p_l_mv", "number_p_l_mv_dv", "long", getSequentialStringArrayWithInts(20));
doTestPointMultiValuedFunctionQuery("number_p_l_mv", "number_p_l_mv_dv", "long",
toStringArray(getRandomLongs(20, false).stream().sorted().collect(Collectors.toList())));
}
@Test
public void testLongPointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
doTestLongPointFieldsAtomicUpdates("number_p_l");
doTestLongPointFieldsAtomicUpdates("number_p_l_dv");
doTestLongPointFieldsAtomicUpdates("number_p_l_dv_ns");
}
@Test
public void testMultiValuedLongPointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
String[] longs = toStringArray(getRandomLongs(3, false));
doTestMultiValuedPointFieldsAtomicUpdates("number_p_l_mv", "long", longs);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_l_ni_mv_dv", "long", longs);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_l_dv_ns_mv", "long", longs);
}
@Test
public void testLongPointSetQuery() throws Exception {
doTestSetQueries("number_p_l", toStringArray(getRandomLongs(20, false)), false);
doTestSetQueries("number_p_l_mv", toStringArray(getRandomLongs(20, false)), true);
doTestSetQueries("number_p_l_ni_dv", toStringArray(getRandomLongs(20, false)), false);
}
@Test
public void testLongPointFieldNotIndexed() throws Exception {
String[] longs = toStringArray(getRandomLongs(10, false));
doTestFieldNotIndexed("number_p_l_ni", longs);
doTestFieldNotIndexed("number_p_l_ni_mv", longs);
}
// Date
private String getRandomDateMaybeWithMath() {
long millis1 = random().nextLong() % MAX_DATE_EPOCH_MILLIS;
String date = Instant.ofEpochMilli(millis1).toString();
if (random().nextBoolean()) {
long millis2 = random().nextLong() % MAX_DATE_EPOCH_MILLIS;
DateGapCeiling gap = new DateGapCeiling(millis2 - millis1);
date += gap.toString();
}
return date;
}
@Test
public void testDatePointFieldExactQuery() throws Exception {
String baseDate = getRandomDateMaybeWithMath();
for (String field : Arrays.asList("number_p_dt","number_p_dt_mv","number_p_dt_dv",
"number_p_dt_mv_dv", "number_p_dt_ni_dv", "number_p_dt_ni_ns_dv", "number_p_dt_ni_mv_dv")) {
doTestDatePointFieldExactQuery(field, baseDate);
}
}
@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 {
int numValues = 10 * RANDOM_MULTIPLIER;
String[] dates = toStringArray(getRandomInstants(numValues, false));
doTestPointFieldReturn("number_p_dt", "date", dates);
doTestPointFieldReturn("number_p_dt_dv_ns", "date", dates);
}
@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", toStringArray(getRandomInstants(1, false)));
doTestPointFieldNonSearchableRangeQuery("number_p_dt_ni_ns", toStringArray(getRandomInstants(1, false)));
int numValues = 2 * RANDOM_MULTIPLIER;
doTestPointFieldNonSearchableRangeQuery("number_p_dt_ni_ns_mv", toStringArray(getRandomInstants(numValues, false)));
}
@Test
public void testDatePointFieldSortAndFunction() throws Exception {
final SortedSet<String> regexToTest = dynFieldRegexesForType(DatePointField.class);
final List<String> sequential = Arrays.asList(getSequentialStringArrayWithDates(10));
final List<Instant> randomDates = getRandomInstants(10, false);
final List<Instant> randomDatesMissing = getRandomInstants(10, true);
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));
String field = r.replace("*", "number");
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomDates);
doTestDatePointFunctionQuery(field);
}
for (String r : Arrays.asList("*_p_dt_smf", "*_p_dt_dv_smf", "*_p_dt_ni_dv_smf",
"*_p_dt_sml", "*_p_dt_dv_sml", "*_p_dt_ni_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomDatesMissing);
doTestDatePointFunctionQuery(field);
}
for (String r : Arrays.asList("*_p_dt_ni", "*_p_dt_ni_ns")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", "1995-12-31T23:59:59Z");
doTestPointFieldFunctionQueryError(field, "w/o docValues", "1995-12-31T23:59:59Z");
}
// multivalued, no docvalues
for (String r : Arrays.asList("*_p_dt_mv", "*_p_dt_ni_mv", "*_p_dt_ni_ns_mv",
"*_p_dt_mv_smf", "*_p_dt_mv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
doTestPointFieldSortError(field, "w/o docValues", "1995-12-31T23:59:59Z");
doTestPointFieldSortError(field, "w/o docValues", "1995-12-31T23:59:59Z", "2000-12-31T23:59:59Z");
doTestPointFieldFunctionQueryError(field, "multivalued", "1995-12-31T23:59:59Z");
doTestPointFieldFunctionQueryError(field, "multivalued", "1995-12-31T23:59:59Z", "2000-12-31T23:59:59Z");
}
// multivalued, w/ docValues
for (String r : Arrays.asList("*_p_dt_ni_mv_dv", "*_p_dt_ni_dv_ns_mv",
"*_p_dt_dv_ns_mv", "*_p_dt_mv_dv",
"*_p_dt_mv_dv_smf", "*_p_dt_ni_mv_dv_smf",
"*_p_dt_mv_dv_sml", "*_p_dt_ni_mv_dv_sml")) {
assertTrue(r, regexToTest.remove(r));
String field = r.replace("*", "number");
// NOTE: only testing one value per doc here, but TestMinMaxOnMultiValuedField
// covers this in more depth
doTestPointFieldSort(field, sequential);
doTestPointFieldSort(field, randomDates);
// value source (w/o field(...,min|max)) usuage should still error...
doTestPointFieldFunctionQueryError(field, "multivalued", "1995-12-31T23:59:59Z");
doTestPointFieldFunctionQueryError(field, "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 {
doTestPointFieldFacetField("number_p_dt", "number_p_dt_dv", getSequentialStringArrayWithDates(10));
clearIndex();
assertU(commit());
doTestPointFieldFacetField("number_p_dt", "number_p_dt_dv", toStringArray(getRandomInstants(10, false)));
}
private static class DateGapCeiling {
String calendarUnit = "MILLIS";
long inCalendarUnits;
boolean negative = false;
/** Maximize calendar unit size given initialGapMillis; performs ceiling on each conversion */
DateGapCeiling(long initialGapMillis) {
negative = initialGapMillis < 0;
inCalendarUnits = Math.abs(initialGapMillis);
if (inCalendarUnits >= 1000L) {
calendarUnit = "SECS";
inCalendarUnits = (inCalendarUnits + 999L) / 1000L;
if (inCalendarUnits >= 60L) {
calendarUnit = "MINUTES";
inCalendarUnits = (inCalendarUnits + 59L) / 60L;
if (inCalendarUnits >= 60L) {
calendarUnit = "HOURS";
inCalendarUnits = (inCalendarUnits + 59L) / 60L;
if (inCalendarUnits >= 24L) {
calendarUnit = "DAYS";
inCalendarUnits = (inCalendarUnits + 23L) / 24L;
if (inCalendarUnits >= 12L) {
calendarUnit = "MONTHS";
inCalendarUnits = (inCalendarUnits + 11L) / 12L;
if ((inCalendarUnits * 16) >= 487) { // 487 = 365.25 / 12 * 16 (365.25 days/year, -ish)
calendarUnit = "YEARS";
inCalendarUnits = (16L * inCalendarUnits + 486) / 487L;
}
}
}
}
}
}
}
@Override
public String toString() {
return (negative ? "-" : "+") + inCalendarUnits + calendarUnit;
}
public long addTo(long millis) { // Instant.plus() doesn't work with estimated durations (MONTHS and YEARS)
LocalDateTime time = LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneOffset.ofHours(0));
if (negative) {
time = time.minus(inCalendarUnits, DateMathParser.CALENDAR_UNITS.get(calendarUnit));
} else {
time = time.plus(inCalendarUnits, DateMathParser.CALENDAR_UNITS.get(calendarUnit));
}
return time.atZone(ZoneOffset.ofHours(0)).toInstant().toEpochMilli();
}
}
@Test
public void testDatePointFieldRangeFacet() throws Exception {
String docValuesField = "number_p_dt_dv";
String nonDocValuesField = "number_p_dt";
int numValues = 10 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Long> values, sortedValues;
long min, max;
DateGapCeiling gap;
do {
values = getRandomLongs(numValues, false, MAX_DATE_EPOCH_MILLIS);
sortedValues = values.stream().sorted().collect(Collectors.toList());
min = sortedValues.get(0);
max = sortedValues.get(sortedValues.size() - 1);
} while (max > MAX_DATE_EPOCH_MILLIS || min < MIN_DATE_EPOCH_MILLIS);
long initialGap = BigInteger.valueOf(max).subtract(BigInteger.valueOf(min))
.divide(BigInteger.valueOf(numBuckets)).longValueExact();
gap = new DateGapCeiling(BigInteger.valueOf(max + initialGap).subtract(BigInteger.valueOf(min)) // padding for rounding
.divide(BigInteger.valueOf(numBuckets)).longValueExact());
int[] bucketCount = new int[numBuckets];
int bucketNum = 0;
long minBucketVal = min;
// System.err.println("min:" + Instant.ofEpochMilli(min) + " max: " + Instant.ofEpochMilli(max) + " gap: " + gap);
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + Instant.ofEpochMilli(minBucketVal));
for (long value : sortedValues) {
// System.err.println("value: " + Instant.ofEpochMilli(value));
while (value >= gap.addTo(minBucketVal)) {
++bucketNum;
minBucketVal = gap.addTo(minBucketVal);
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + Instant.ofEpochMilli(minBucketVal));
}
++bucketCount[bucketNum];
}
for (int i = 0 ; i < numValues ; i++) {
assertU(adoc("id", String.valueOf(i), docValuesField, Instant.ofEpochMilli(values.get(i)).toString(),
nonDocValuesField, Instant.ofEpochMilli(values.get(i)).toString()));
}
assertU(commit());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
String[] testStrings = new String[numBuckets + 1];
testStrings[numBuckets] = "//*[@numFound='" + numValues + "']";
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + Instant.ofEpochMilli(minBucketVal)
+ "'][.='" + bucketCount[i] + "']";
minBucketVal = gap.addTo(minBucketVal);
}
long maxPlusGap = gap.addTo(max);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", Instant.ofEpochMilli(min).toString(),
"facet.range.end", Instant.ofEpochMilli(maxPlusGap).toString(),
"facet.range.gap", gap.toString()),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", Instant.ofEpochMilli(min).toString(),
"facet.range.end", Instant.ofEpochMilli(maxPlusGap).toString(),
"facet.range.gap", gap.toString(),
"facet.range.method", "dv"),
testStrings);
assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + Instant.ofEpochMilli(minBucketVal).toString()
+ "'][.='" + bucketCount[i] + "']";
minBucketVal = gap.addTo(minBucketVal);
}
maxPlusGap = gap.addTo(max);
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", Instant.ofEpochMilli(min).toString(),
"facet.range.end", Instant.ofEpochMilli(maxPlusGap).toString(),
"facet.range.gap", gap.toString(),
"facet.range.method", "filter"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", Instant.ofEpochMilli(min).toString(),
"facet.range.end", Instant.ofEpochMilli(maxPlusGap).toString(),
"facet.range.gap", gap.toString(),
"facet.range.method", "dv"),
testStrings);
}
@Test
public void testDatePointStats() throws Exception {
String[] randomSortedDates = toAscendingStringArray(getRandomInstants(10, false), true);
doTestDatePointStats("number_p_dt", "number_p_dt_dv", randomSortedDates);
doTestDatePointStats("number_p_dt_mv", "number_p_dt_mv_dv", randomSortedDates);
}
@Test
public void testDatePointFieldMultiValuedExactQuery() throws Exception {
String[] dates = toStringArray(getRandomInstants(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_dt_mv", dates);
doTestPointFieldMultiValuedExactQuery("number_p_dt_ni_mv_dv", dates);
}
@Test
public void testDatePointFieldMultiValuedNonSearchableExactQuery() throws Exception {
String[] dates = toStringArray(getRandomInstants(20, false));
doTestPointFieldMultiValuedExactQuery("number_p_dt_ni_mv", dates, false);
doTestPointFieldMultiValuedExactQuery("number_p_dt_ni_ns_mv", dates, false);
}
@Test
public void testDatePointFieldMultiValuedReturn() throws Exception {
String[] dates = toStringArray(getRandomInstants(20, false));
doTestPointFieldMultiValuedReturn("number_p_dt_mv", "date", dates);
doTestPointFieldMultiValuedReturn("number_p_dt_ni_mv_dv", "date", dates);
doTestPointFieldMultiValuedReturn("number_p_dt_dv_ns_mv", "date", dates);
}
@Test
public void testDatePointFieldMultiValuedRangeQuery() throws Exception {
String[] dates = toStringArray(getRandomInstants(20, false).stream().sorted().collect(Collectors.toList()));
doTestPointFieldMultiValuedRangeQuery("number_p_dt_mv", "date", dates);
doTestPointFieldMultiValuedRangeQuery("number_p_dt_ni_mv_dv", "date", dates);
}
@Test
public void testDatePointFieldMultiValuedFacetField() throws Exception {
doTestPointFieldMultiValuedFacetField("number_p_dt_mv", "number_p_dt_mv_dv", getSequentialStringArrayWithDates(20));
doTestPointFieldMultiValuedFacetField("number_p_dt_mv", "number_p_dt_mv_dv", toStringArray(getRandomInstants(20, false)));
}
@Test
public void testDatePointFieldMultiValuedRangeFacet() throws Exception {
String docValuesField = "number_p_dt_mv_dv";
SchemaField dvSchemaField = h.getCore().getLatestSchema().getField(docValuesField);
assertTrue(dvSchemaField.multiValued());
assertTrue(dvSchemaField.hasDocValues());
assertTrue(dvSchemaField.getType() instanceof PointField);
String nonDocValuesField = "number_p_dt_mv";
SchemaField nonDvSchemaField = h.getCore().getLatestSchema().getField(nonDocValuesField);
assertTrue(nonDvSchemaField.multiValued());
assertFalse(nonDvSchemaField.hasDocValues());
assertTrue(nonDvSchemaField.getType() instanceof PointField);
int numValues = 20 * RANDOM_MULTIPLIER;
int numBuckets = numValues / 2;
List<Long> values;
List<PosVal<Long>> sortedValues;
long min, max;
do {
values = getRandomLongs(numValues, false, MAX_DATE_EPOCH_MILLIS);
sortedValues = toAscendingPosVals(values, true);
min = sortedValues.get(0).val;
max = sortedValues.get(sortedValues.size() - 1).val;
} while (max > MAX_DATE_EPOCH_MILLIS || min < MIN_DATE_EPOCH_MILLIS);
long initialGap = BigInteger.valueOf(max).subtract(BigInteger.valueOf(min))
.divide(BigInteger.valueOf(numBuckets)).longValueExact();
DateGapCeiling gap = new DateGapCeiling(BigInteger.valueOf(max + initialGap).subtract(BigInteger.valueOf(min)) // padding for rounding
.divide(BigInteger.valueOf(numBuckets)).longValueExact());
List<Set<Integer>> docIdBucket = new ArrayList<>(numBuckets);
for (int i = 0 ; i < numBuckets ; ++i) {
docIdBucket.add(new HashSet<>());
}
int bucketNum = 0;
long minBucketVal = min;
// System.err.println("min:" + Instant.ofEpochMilli(min) + " max: " + Instant.ofEpochMilli(max) + " gap: " + gap);
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + Instant.ofEpochMilli(minBucketVal));
for (PosVal<Long> value : sortedValues) {
// System.err.println("value: " + Instant.ofEpochMilli(value.val));
while (value.val >= gap.addTo(minBucketVal)) {
++bucketNum;
minBucketVal = gap.addTo(minBucketVal);
// System.err.println("bucketNum: " + bucketNum + " minBucketVal: " + Instant.ofEpochMilli(minBucketVal));
}
docIdBucket.get(bucketNum).add(value.pos / 2); // each doc gets two consecutive values
}
for (int i = 0 ; i < numValues ; i += 2) {
assertU(adoc("id", String.valueOf(i / 2),
docValuesField, Instant.ofEpochMilli(values.get(i)).toString(),
docValuesField, Instant.ofEpochMilli(values.get(i + 1)).toString(),
nonDocValuesField, Instant.ofEpochMilli(values.get(i)).toString(),
nonDocValuesField, Instant.ofEpochMilli(values.get(i + 1)).toString()));
}
assertU(commit());
String minDate = Instant.ofEpochMilli(min).toString();
String maxDate = Instant.ofEpochMilli(max).toString();
String[] testStrings = new String[numBuckets + 1];
testStrings[numBuckets] = "//*[@numFound='" + (numValues / 2) + "']";
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal = gap.addTo(minBucketVal), ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + docValuesField
+ "']/lst[@name='counts']/int[@name='" + Instant.ofEpochMilli(minBucketVal)
+ "'][.='" + docIdBucket.get(i).size() + "']";
}
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", minDate, "facet.range.end", maxDate,
"facet.range.gap", gap.toString(), "indent", "on"),
testStrings);
assertQ(req("q", "*:*", "facet", "true", "facet.range", docValuesField,
"facet.range.start", minDate, "facet.range.end", maxDate,
"facet.range.gap", gap.toString(), "facet.range.method", "dv", "indent", "on"),
testStrings);
minBucketVal = min;
for (int i = 0 ; i < numBuckets ; minBucketVal = gap.addTo(minBucketVal), ++i) {
testStrings[i] = "//lst[@name='facet_counts']/lst[@name='facet_ranges']/lst[@name='" + nonDocValuesField
+ "']/lst[@name='counts']/int[@name='" + Instant.ofEpochMilli(minBucketVal)
+ "'][.='" + docIdBucket.get(i).size() + "']";
}
// Range Faceting with method = filter should work
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", minDate, "facet.range.end", maxDate,
"facet.range.gap", gap.toString(), "facet.range.method", "filter", "indent", "on"),
testStrings);
// this should actually use filter method instead of dv
assertQ(req("q", "*:*", "facet", "true", "facet.range", nonDocValuesField,
"facet.range.start", minDate, "facet.range.end", maxDate,
"facet.range.gap", gap.toString(), "facet.range.method", "dv", "indent", "on"),
testStrings);
}
@Test
public void testDatePointMultiValuedFunctionQuery() throws Exception {
String[] dates = toStringArray(getRandomInstants(20, false).stream().sorted().collect(Collectors.toList()));
doTestPointMultiValuedFunctionQuery("number_p_dt_mv", "number_p_dt_mv_dv", "date", dates);
}
@Test
public void testDatePointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
doTestDatePointFieldsAtomicUpdates("number_p_dt");
doTestDatePointFieldsAtomicUpdates("number_p_dt_dv");
doTestDatePointFieldsAtomicUpdates("number_p_dt_dv_ns");
}
@Test
public void testMultiValuedDatePointFieldsAtomicUpdates() throws Exception {
if (!Boolean.getBoolean("enable.update.log")) {
return;
}
List<String> datesList = getRandomLongs(3, false, MAX_DATE_EPOCH_MILLIS)
.stream().map(Instant::ofEpochMilli).map(Object::toString).collect(Collectors.toList());
String[] dates = datesList.toArray(new String[datesList.size()]);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_dt_mv", "date", dates);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_dt_ni_mv_dv", "date", dates);
doTestMultiValuedPointFieldsAtomicUpdates("number_p_dt_dv_ns_mv", "date", dates);
}
@Test
public void testDatePointSetQuery() throws Exception {
doTestSetQueries("number_p_dt", toStringArray(getRandomInstants(20, false)), false);
doTestSetQueries("number_p_dt_mv", toStringArray(getRandomInstants(20, false)), true);
doTestSetQueries("number_p_dt_ni_dv", toStringArray(getRandomInstants(20, false)), false);
}
@Test
public void testDatePointFieldNotIndexed() throws Exception {
String[] dates = toStringArray(getRandomInstants(10, false));
doTestFieldNotIndexed("number_p_dt_ni", dates);
doTestFieldNotIndexed("number_p_dt_ni_mv", dates);
}
@Test
public void testIndexOrDocValuesQuery() throws Exception {
String[] fieldTypeNames = new String[] { "_p_i", "_p_l", "_p_d", "_p_f", "_p_dt" };
FieldType[] fieldTypes = new FieldType[]
{ new IntPointField(), new LongPointField(), new DoublePointField(), new FloatPointField(), new DatePointField() };
String[] ints = toStringArray(getRandomInts(2, false).stream().sorted().collect(Collectors.toList()));
String[] longs = toStringArray(getRandomLongs(2, false).stream().sorted().collect(Collectors.toList()));
String[] doubles = toStringArray(getRandomDoubles(2, false).stream().sorted().collect(Collectors.toList()));
String[] floats = toStringArray(getRandomFloats(2, false).stream().sorted().collect(Collectors.toList()));
String[] dates = toStringArray(getRandomInstants(2, false).stream().sorted().collect(Collectors.toList()));
String[] min = new String[] { ints[0], longs[0], doubles[0], floats[0], dates[0] };
String[] max = new String[] { ints[1], longs[1], doubles[1], floats[1], dates[1] };
assert fieldTypeNames.length == fieldTypes.length
&& fieldTypeNames.length == max.length
&& fieldTypeNames.length == min.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, min[i], max[i], true, true) instanceof PointRangeQuery);
assertTrue(fieldTypes[i].getRangeQuery(null, fieldIndexedAndDv, min[i], max[i], true, true) instanceof IndexOrDocValuesQuery);
assertTrue(fieldTypes[i].getRangeQuery(null, fieldIndexedMv, min[i], max[i], true, true) instanceof PointRangeQuery);
assertTrue(fieldTypes[i].getRangeQuery(null, fieldIndexedAndDvMv, min[i], max[i], true, true) instanceof IndexOrDocValuesQuery);
assertTrue(fieldTypes[i].getFieldQuery(null, fieldIndexed, min[i]) instanceof PointRangeQuery);
assertTrue(fieldTypes[i].getFieldQuery(null, fieldIndexedAndDv, min[i]) instanceof IndexOrDocValuesQuery);
assertTrue(fieldTypes[i].getFieldQuery(null, fieldIndexedMv, min[i]) instanceof PointRangeQuery);
assertTrue(fieldTypes[i].getFieldQuery(null, fieldIndexedAndDvMv, min[i]) instanceof IndexOrDocValuesQuery);
}
}
public void testInternals() throws IOException {
String[] types = new String[]{"i", "l", "f", "d", "dt"};
String[][] values = new String[][] {
toStringArray(getRandomInts(10, false)),
toStringArray(getRandomLongs(10, false)),
toStringArray(getRandomFloats(10, false)),
toStringArray(getRandomDoubles(10, false)),
toStringArray(getRandomInstants(10, false))
};
assertEquals(types.length, values.length);
Set<String> typesTested = new HashSet<>();
for (int i = 0 ; i < types.length ; ++i) {
for (String suffix:FIELD_SUFFIXES) {
doTestInternals("number_p_" + types[i] + suffix, values[i]);
typesTested.add("*_p_" + types[i] + 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 <T> List<T> getRandomList(int length, boolean missingVals, Supplier<T> randomVal) {
List<T> list = new ArrayList<>(length);
for (int i = 0 ; i < length ; ++i) {
T val = null;
// Sometimes leave val as null when we're producing missing values
if (missingVals == false || usually()) {
val = randomVal.get();
}
list.add(val);
}
return list;
}
private List<Double> getRandomDoubles(int length, boolean missingVals) {
return getRandomList(length, missingVals, () -> {
Double d = Double.NaN;
while (d.isNaN()) {
d = Double.longBitsToDouble(random().nextLong());
}
return d;
});
}
private List<Float> getRandomFloats(int length, boolean missingVals) {
return getRandomList(length, missingVals, () -> {
Float f = Float.NaN;
while (f.isNaN()) {
f = Float.intBitsToFloat(random().nextInt());
}
return f;
});
}
private List<Integer> getRandomInts(int length, boolean missingVals, int boundPosNeg) {
assert boundPosNeg > 0L;
return getRandomList(length, missingVals,
() -> (random().nextBoolean() ? 1 : -1) * random().nextInt(boundPosNeg));
}
private List<Integer> getRandomInts(int length, boolean missingVals) {
return getRandomList(length, missingVals, () -> random().nextInt());
}
private List<Long> getRandomLongs(int length, boolean missingVals, long boundPosNeg) {
assert boundPosNeg > 0L;
return getRandomList(length, missingVals,
() -> random().nextLong() % boundPosNeg); // see Random.nextInt(int bound)
}
private List<Long> getRandomLongs(int length, boolean missingVals) {
return getRandomList(length, missingVals, () -> random().nextLong());
}
private List<Instant> getRandomInstants(int length, boolean missingVals) {
return getRandomList(length, missingVals, () -> Instant.ofEpochMilli(random().nextLong()));
}
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 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']");
h.getCore().withSearcher(searcher -> {
IndexReader ir = searcher.getIndexReader();
assertEquals("Field " + field + " should have no point values", 0, PointValues.size(ir, field));
return null;
});
}
private void doTestIntPointFieldExactQuery(final String field, final boolean testLong) throws Exception {
doTestIntPointFieldExactQuery(field, testLong, true);
}
private String getTestString(boolean searchable, int numFound) {
return "//*[@numFound='" + (searchable ? Integer.toString(numFound) : "0") + "']";
}
/**
* @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 {
int numValues = 10 * RANDOM_MULTIPLIER;
Map<String,Integer> randCount = new HashMap<>(numValues);
String[] rand = testLong ? toStringArray(getRandomLongs(numValues, false))
: toStringArray(getRandomInts(numValues, false));
for (int i = 0 ; i < numValues ; i++) {
randCount.merge(rand[i], 1, (a, b) -> a + b); // count unique values
assertU(adoc("id", String.valueOf(i), field, rand[i]));
}
assertU(commit());
for (int i = 0 ; i < numValues ; i++) {
assertQ(req("q", field + ":" + (rand[i].startsWith("-") ? "\\" : "") + rand[i],
"fl", "id," + field), getTestString(searchable, randCount.get(rand[i])));
}
StringBuilder builder = new StringBuilder();
for (String value : randCount.keySet()) {
if (builder.length() != 0) {
builder.append(" OR ");
}
if (value.startsWith("-")) {
builder.append("\\"); // escape negative sign
}
builder.append(value);
}
assertQ(req("debug", "true", "q", field + ":(" + builder.toString() + ")"), getTestString(searchable, numValues));
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), getTestString(searchable, 1));
clearIndex();
assertU(commit());
}
private void doTestPointFieldReturn(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 = toAscendingStringArray(getRandomLongs(100, false), true);
} else {
arr = toAscendingStringArray(getRandomInts(100, false), 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']");
}
if (testLong) {
assertQ(req("q", fieldName + ":[" + Long.MIN_VALUE + " TO " + Long.MIN_VALUE + "}", "fl", "id, " + fieldName),
"//*[@numFound='0']");
assertQ(req("q", fieldName + ":{" + Long.MAX_VALUE + " TO " + Long.MAX_VALUE + "]", "fl", "id, " + fieldName),
"//*[@numFound='0']");
} else {
assertQ(req("q", fieldName + ":[" + Integer.MIN_VALUE + " TO " + Integer.MIN_VALUE + "}", "fl", "id, " + fieldName),
"//*[@numFound='0']");
assertQ(req("q", fieldName + ":{" + Integer.MAX_VALUE + " TO " + Integer.MAX_VALUE + "]", "fl", "id, " + fieldName),
"//*[@numFound='0']");
}
}
private void doTestPointFieldFacetField(String nonDocValuesField, String docValuesField, String[] numbers) throws Exception {
assert numbers != null && numbers.length == 10;
assertFalse(h.getCore().getLatestSchema().getField(docValuesField).multiValued());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(docValuesField).getType() instanceof PointField);
for (int i = 0; i < 10; i++) {
assertU(adoc("id", String.valueOf(i), docValuesField, numbers[i], nonDocValuesField, numbers[i]));
}
assertU(commit());
assertQ(req("q", "*:*", "fl", "id, " + docValuesField, "facet", "true", "facet.field", docValuesField),
"//*[@numFound='10']",
"//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[1] + "'][.='1']",
"//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[2] + "'][.='1']",
"//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[3] + "'][.='1']");
assertU(adoc("id", "10", docValuesField, numbers[1], nonDocValuesField, numbers[1]));
assertU(commit());
assertQ(req("q", "*:*", "fl", "id, " + docValuesField, "facet", "true", "facet.field", docValuesField),
"//*[@numFound='11']",
"//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[1] + "'][.='2']",
"//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[2] + "'][.='1']",
"//lst[@name='facet_counts']/lst[@name='facet_fields']/lst[@name='" + docValuesField +"']/int[@name='" + numbers[3] + "'][.='1']");
assertFalse(h.getCore().getLatestSchema().getField(nonDocValuesField).hasDocValues());
assertTrue(h.getCore().getLatestSchema().getField(nonDocValuesField).getType() instanceof PointField);
assertQEx("Expecting Exception",
"Can't facet on a PointField without docValues",
req("q", "*:*", "fl", "id, " + nonDocValuesField, "facet", "true", "facet.field", nonDocValuesField),
SolrException.ErrorCode.BAD_REQUEST);
}
private void doTestIntPointFunctionQuery(String field) throws Exception {
assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
int numVals = 10 * RANDOM_MULTIPLIER;
List<Integer> values = getRandomInts(numVals, false);
String assertNumFound = "//*[@numFound='" + numVals + "']";
String[] idAscXpathChecks = new String[numVals + 1];
String[] idAscNegXpathChecks = new String[numVals + 1];
idAscXpathChecks[0] = assertNumFound;
idAscNegXpathChecks[0] = assertNumFound;
for (int i = 0 ; i < values.size() ; ++i) {
assertU(adoc("id", Character.valueOf((char)('A' + i)).toString(), field, String.valueOf(values.get(i))));
// reminder: xpath array indexes start at 1
idAscXpathChecks[i + 1] = "//result/doc[" + (1 + i) + "]/int[@name='field(" + field + ")'][.='" + values.get(i) + "']";
idAscNegXpathChecks[i + 1] = "//result/doc[" + (1 + i) + "]/float[@name='product(-1," + field + ")'][.='"
+ (-1.0f * (float)values.get(i)) + "']";
}
assertU(commit());
assertQ(req("q", "*:*", "fl", "id, " + field + ", field(" + field + ")", "rows", String.valueOf(numVals), "sort", "id asc"),
idAscXpathChecks);
assertQ(req("q", "*:*", "fl", "id, " + field + ", product(-1," + field + ")", "rows", String.valueOf(numVals), "sort", "id asc"),
idAscNegXpathChecks);
List<PosVal<Integer>> ascNegPosVals
= toAscendingPosVals(values.stream().map(v -> -v).collect(Collectors.toList()), true);
String[] ascNegXpathChecks = new String[numVals + 1];
ascNegXpathChecks[0] = assertNumFound;
for (int i = 0 ; i < ascNegPosVals.size() ; ++i) {
PosVal<Integer> posVal = ascNegPosVals.get(i);
ascNegXpathChecks[i + 1]
= "//result/doc[" + (1 + i) + "]/int[@name='" + field + "'][.='" + values.get(posVal.pos) + "']";
}
assertQ(req("q", "*:*", "fl", "id, " + field, "rows", String.valueOf(numVals), "sort", "product(-1," + field + ") asc"),
ascNegXpathChecks);
clearIndex();
assertU(commit());
}
private void doTestLongPointFunctionQuery(String field) throws Exception {
assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
int numVals = 10 * RANDOM_MULTIPLIER;
List<Long> values = getRandomLongs(numVals, false);
String assertNumFound = "//*[@numFound='" + numVals + "']";
String[] idAscXpathChecks = new String[numVals + 1];
String[] idAscNegXpathChecks = new String[numVals + 1];
idAscXpathChecks[0] = assertNumFound;
idAscNegXpathChecks[0] = assertNumFound;
for (int i = 0 ; i < values.size() ; ++i) {
assertU(adoc("id", Character.valueOf((char)('A' + i)).toString(), field, String.valueOf(values.get(i))));
// reminder: xpath array indexes start at 1
idAscXpathChecks[i + 1] = "//result/doc[" + (1 + i) + "]/long[@name='field(" + field + ")'][.='" + values.get(i) + "']";
idAscNegXpathChecks[i + 1] = "//result/doc[" + (1 + i) + "]/float[@name='product(-1," + field + ")'][.='"
+ (-1.0f * (float)values.get(i)) + "']";
}
assertU(commit());
assertQ(req("q", "*:*", "fl", "id, " + field + ", field(" + field + ")", "rows", String.valueOf(numVals), "sort", "id asc"),
idAscXpathChecks);
assertQ(req("q", "*:*", "fl", "id, " + field + ", product(-1," + field + ")", "rows", String.valueOf(numVals), "sort", "id asc"),
idAscNegXpathChecks);
List<PosVal<Long>> ascNegPosVals
= toAscendingPosVals(values.stream().map(v -> -v).collect(Collectors.toList()), true);
String[] ascNegXpathChecks = new String[numVals + 1];
ascNegXpathChecks[0] = assertNumFound;
for (int i = 0 ; i < ascNegPosVals.size() ; ++i) {
PosVal<Long> posVal = ascNegPosVals.get(i);
ascNegXpathChecks[i + 1]
= "//result/doc[" + (1 + i) + "]/long[@name='" + field + "'][.='" + values.get(posVal.pos) + "']";
}
assertQ(req("q", "*:*", "fl", "id, " + field, "rows", String.valueOf(numVals), "sort", "product(-1," + field + ") asc"),
ascNegXpathChecks);
clearIndex();
assertU(commit());
}
/**
* 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 doTestPointStats(String field, String dvField, String[] numbers, double min, double max, int count, int 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='" + (numbers.length + 1) + "']",
"//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 doTestPointFieldMultiValuedExactQuery(final String fieldName, final String[] numbers) throws Exception {
doTestPointFieldMultiValuedExactQuery(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 doTestPointFieldMultiValuedExactQuery(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());
FieldType type = h.getCore().getLatestSchema().getField(fieldName).getType();
for (int i = 0; i < 20; i++) {
if (type 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 (type 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 doTestPointFieldMultiValuedReturn(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 + "[.='" + numbers[i] + "']",
"//doc/arr[@name='" + fieldName + "']/" + type + "[.='" + 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 + "[.='" + numbers[i] + "']",
"//doc/arr[@name='" + fieldName + "']/" + type + "[.='" + 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 + "[.='" + numbers[i-1] + "']";
expected[i+10] = "//result/doc[" + i + "]/arr[@name='" + fieldName + "']/" + type + "[.='" + numbers[i + 9] + "']";
}
assertQ(req("q", "*:*", "fl", "id, " + fieldName, "sort","id asc"), expected);
}
private void doTestPointFieldMultiValuedRangeQuery(String fieldName, String type, String[] numbers) throws Exception {
assert numbers != null && numbers.length == 20;
SchemaField sf = h.getCore().getLatestSchema().getField(fieldName);
assertTrue(sf.multiValued());
assertTrue(sf.getType() instanceof PointField);
for (int i=9; i >= 0; i--) {
assertU(adoc("id", String.valueOf(i), fieldName, numbers[i], fieldName, numbers[i+10]));
}
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] + "']");
if (sf.getType().getNumberType() == NumberType.FLOAT || sf.getType().getNumberType() == NumberType.DOUBLE) {
doTestDoubleFloatRangeLimits(fieldName, sf.getType().getNumberType() == NumberType.DOUBLE);
}
}
private void doTestPointFieldMultiValuedFacetField(String nonDocValuesField, String dvFieldName, String[] numbers) throws Exception {
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 doTestPointMultiValuedFunctionQuery(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, " + 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 doTestMultiValuedPointFieldsAtomicUpdates(String field, String type, String[] values) throws Exception {
assertEquals(3, values.length);
assertU(adoc(sdoc("id", "1", field, String.valueOf(values[0]))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + values[0] + "']",
"count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=1");
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("add", values[1]))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + values[0] + "']",
"//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + values[1] + "']",
"count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=2");
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("remove", values[0]))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + values[1] + "']",
"count(//result/doc[1]/arr[@name='" + field + "']/" + type + ")=1");
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", Arrays.asList(values)))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + values[0] + "']",
"//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + values[1] + "']",
"//result/doc[1]/arr[@name='" + field + "']/" + type + "[.='" + values[2] + "']",
"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 doTestIntPointFieldsAtomicUpdates(String field) throws Exception {
int number1 = random().nextInt();
int number2;
long inc1;
for ( ; ; ) {
number2 = random().nextInt();
inc1 = number2 - number1;
if (Math.abs(inc1) < (long)Integer.MAX_VALUE) {
break;
}
}
assertU(adoc(sdoc("id", "1", field, String.valueOf(number1))));
assertU(commit());
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", (int)inc1))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/int[@name='" + field + "'][.='" + number2 + "']");
int number3 = random().nextInt();
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", number3))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/int[@name='" + field + "'][.='" + number3 + "']");
}
private void doTestLongPointFieldsAtomicUpdates(String field) throws Exception {
long number1 = random().nextLong();
long number2;
BigInteger inc1;
for ( ; ; ) {
number2 = random().nextLong();
inc1 = BigInteger.valueOf(number2).subtract(BigInteger.valueOf(number1));
if (inc1.abs().compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) {
break;
}
}
assertU(adoc(sdoc("id", "1", field, String.valueOf(number1))));
assertU(commit());
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("inc", inc1.longValueExact()))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/long[@name='" + field + "'][.='" + number2 + "']");
long number3 = random().nextLong();
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", number3))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/long[@name='" + field + "'][.='" + number3 + "']");
}
private void doTestFloatPointFieldExactQuery(final String field, boolean testDouble) throws Exception {
doTestFloatPointFieldExactQuery(field, true, testDouble);
}
/**
* @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(final String field, final boolean searchable, final boolean testDouble)
throws Exception {
int numValues = 10 * RANDOM_MULTIPLIER;
Map<String,Integer> randCount = new HashMap<>(numValues);
String[] rand = testDouble ? toStringArray(getRandomDoubles(numValues, false))
: toStringArray(getRandomFloats(numValues, false));
for (int i = 0 ; i < numValues ; i++) {
randCount.merge(rand[i], 1, (a, b) -> a + b); // count unique values
assertU(adoc("id", String.valueOf(i), field, rand[i]));
}
assertU(commit());
for (int i = 0 ; i < numValues ; i++) {
assertQ(req("q", field + ":" + (rand[i].startsWith("-") ? "\\" : "") + rand[i],
"fl", "id," + field), getTestString(searchable, randCount.get(rand[i])));
}
StringBuilder builder = new StringBuilder();
for (String value : randCount.keySet()) {
if (builder.length() != 0) {
builder.append(" OR ");
}
if (value.startsWith("-")) {
builder.append("\\"); // escape negative sign
}
builder.append(value);
}
assertQ(req("debug", "true", "q", field + ":(" + builder.toString() + ")"), getTestString(searchable, numValues));
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 <T extends Comparable<T>> void doTestPointFieldSort(String field, List<T> values) throws Exception {
assert values != null && 2 <= values.size();
final List<SolrInputDocument> docs = new ArrayList<>(values.size());
final String[] ascXpathChecks = new String[values.size() + 1];
final String[] descXpathChecks = new String[values.size() + 1];
ascXpathChecks[values.size()] = "//*[@numFound='" + values.size() + "']";
descXpathChecks[values.size()] = "//*[@numFound='" + values.size() + "']";
boolean missingFirst = field.endsWith("_sml") == false;
List<PosVal<T>> ascendingPosVals = toAscendingPosVals(values, missingFirst);
for (int i = ascendingPosVals.size() - 1 ; i >= 0 ; --i) {
T value = ascendingPosVals.get(i).val;
if (value == null) {
docs.add(sdoc("id", String.valueOf(i))); // null => missing value
} else {
docs.add(sdoc("id", String.valueOf(i), field, String.valueOf(value)));
}
// reminder: xpath array indexes start at 1
ascXpathChecks[i]= "//result/doc["+ (1 + i)+"]/str[@name='id'][.='"+i+"']";
}
List<PosVal<T>> descendingPosVals = toDescendingPosVals
(ascendingPosVals.stream().map(pv->pv.val).collect(Collectors.toList()), missingFirst);
for (int i = descendingPosVals.size() - 1 ; i >= 0 ; --i) {
descXpathChecks[i]= "//result/doc[" + (i + 1) + "]/str[@name='id'][.='" + descendingPosVals.get(i).pos + "']";
}
// 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, " + field, "sort", field + " asc, id asc"),
ascXpathChecks);
assertQ(req("q", "*:*", "fl", "id, " + field, "sort", field + " desc, id 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 = toAscendingStringArray(getRandomDoubles(10, false), true);
} else {
arr = toAscendingStringArray(getRandomFloats(10, false), 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)) + "']");
}
doTestDoubleFloatRangeLimits(fieldName, testDouble);
}
private void doTestDoubleFloatRangeLimits(String fieldName, boolean testDouble) {
// POSITIVE/NEGATIVE_INFINITY toString is the same for Double and Float, it's OK to use this code for both cases
String positiveInfinity = String.valueOf(Double.POSITIVE_INFINITY);
String negativeInfinity = String.valueOf(Double.NEGATIVE_INFINITY);
String minVal = String.valueOf(testDouble?Double.MIN_VALUE:Float.MIN_VALUE);
String maxVal = String.valueOf(testDouble?Double.MAX_VALUE:Float.MAX_VALUE);
String negativeMinVal = "-" + minVal;
String negativeMaxVal = "-" + maxVal;
clearIndex();
assertU(adoc("id", "1", fieldName, minVal));
assertU(adoc("id", "2", fieldName, maxVal));
assertU(adoc("id", "3", fieldName, negativeInfinity));
assertU(adoc("id", "4", fieldName, positiveInfinity));
assertU(adoc("id", "5", fieldName, negativeMinVal));
assertU(adoc("id", "6", fieldName, negativeMaxVal));
assertU(commit());
//negative to negative
assertAllInclusiveExclusiveVariations(fieldName, "*", "-1", 2, 2, 2, 2);
assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, "-1", 1, 2, 1, 2);
assertAllInclusiveExclusiveVariations(fieldName, negativeMaxVal, negativeMinVal, 0, 1, 1, 2);
//negative to cero
assertAllInclusiveExclusiveVariations(fieldName, "*", "-0.0f", 3, 3, 3, 3);
assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, "-0.0f", 2, 3, 2, 3);
assertAllInclusiveExclusiveVariations(fieldName, negativeMinVal, "-0.0f", 0, 1, 0, 1);
assertAllInclusiveExclusiveVariations(fieldName, "*", "0", 3, 3, 3, 3);
assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, "0", 2, 3, 2, 3);
assertAllInclusiveExclusiveVariations(fieldName, negativeMinVal, "0", 0, 1, 0, 1);
//negative to positive
assertAllInclusiveExclusiveVariations(fieldName, "*", "1", 4, 4, 4, 4);
assertAllInclusiveExclusiveVariations(fieldName, "-1", "*", 4, 4, 4, 4);
assertAllInclusiveExclusiveVariations(fieldName, "-1", "1", 2, 2, 2, 2);
assertAllInclusiveExclusiveVariations(fieldName, "*", "*", 6, 6, 6, 6);
assertAllInclusiveExclusiveVariations(fieldName, "-1", positiveInfinity, 3, 3, 4, 4);
assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, "1", 3, 4, 3, 4);
assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, positiveInfinity, 4, 5, 5, 6);
assertAllInclusiveExclusiveVariations(fieldName, negativeMinVal, minVal, 0, 1, 1, 2);
assertAllInclusiveExclusiveVariations(fieldName, negativeMaxVal, maxVal, 2, 3, 3, 4);
//cero to positive
assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", "*", 3, 3, 3, 3);
assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", positiveInfinity, 2, 2, 3, 3);
assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", minVal, 0, 0, 1, 1);
assertAllInclusiveExclusiveVariations(fieldName, "0", "*", 3, 3, 3, 3);
assertAllInclusiveExclusiveVariations(fieldName, "0", positiveInfinity, 2, 2, 3, 3);
assertAllInclusiveExclusiveVariations(fieldName, "0", minVal, 0, 0, 1, 1);
//positive to positive
assertAllInclusiveExclusiveVariations(fieldName, "1", "*", 2, 2, 2, 2);
assertAllInclusiveExclusiveVariations(fieldName, "1", positiveInfinity, 1, 1, 2, 2);
assertAllInclusiveExclusiveVariations(fieldName, minVal, maxVal, 0, 1, 1, 2);
// inverted limits
assertAllInclusiveExclusiveVariations(fieldName, "1", "-1", 0, 0, 0, 0);
assertAllInclusiveExclusiveVariations(fieldName, positiveInfinity, negativeInfinity, 0, 0, 0, 0);
assertAllInclusiveExclusiveVariations(fieldName, minVal, negativeMinVal, 0, 0, 0, 0);
// MatchNoDocs cases
assertAllInclusiveExclusiveVariations(fieldName, negativeInfinity, negativeInfinity, 0, 0, 0, 1);
assertAllInclusiveExclusiveVariations(fieldName, positiveInfinity, positiveInfinity, 0, 0, 0, 1);
clearIndex();
assertU(adoc("id", "1", fieldName, "0.0"));
assertU(adoc("id", "2", fieldName, "-0.0"));
assertU(commit());
assertAllInclusiveExclusiveVariations(fieldName, "*", "*", 2, 2, 2, 2);
assertAllInclusiveExclusiveVariations(fieldName, "*", "0", 1, 1, 2, 2);
assertAllInclusiveExclusiveVariations(fieldName, "0", "*", 0, 1, 0, 1);
assertAllInclusiveExclusiveVariations(fieldName, "*", "-0.0f", 0, 0, 1, 1);
assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", "*", 1, 2, 1, 2);
assertAllInclusiveExclusiveVariations(fieldName, "-0.0f", "0", 0, 1, 1, 2);
}
private void assertAllInclusiveExclusiveVariations(String fieldName, String min, String max,
int countExclusiveExclusive,
int countInclusiveExclusive,
int countExclusiveInclusive,
int countInclusiveInclusive) {
assertQ(req("q", fieldName + ":{" + min + " TO " + max + "}", "fl", "id, " + fieldName),
"//*[@numFound='" + countExclusiveExclusive +"']");
assertQ(req("q", fieldName + ":[" + min + " TO " + max + "}", "fl", "id, " + fieldName),
"//*[@numFound='" + countInclusiveExclusive +"']");
assertQ(req("q", fieldName + ":{" + min + " TO " + max + "]", "fl", "id, " + fieldName),
"//*[@numFound='" + countExclusiveInclusive +"']");
assertQ(req("q", fieldName + ":[" + min + " TO " + max + "]", "fl", "id, " + fieldName),
"//*[@numFound='" + countInclusiveInclusive +"']");
}
private void doTestFloatPointFunctionQuery(String field) throws Exception {
assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
int numVals = 10 * RANDOM_MULTIPLIER;
List<Float> values = getRandomFloats(numVals, false);
String assertNumFound = "//*[@numFound='" + numVals + "']";
String[] idAscXpathChecks = new String[numVals + 1];
String[] idAscNegXpathChecks = new String[numVals + 1];
idAscXpathChecks[0] = assertNumFound;
idAscNegXpathChecks[0] = assertNumFound;
for (int i = 0 ; i < values.size() ; ++i) {
assertU(adoc("id", Character.valueOf((char)('A' + i)).toString(), field, String.valueOf(values.get(i))));
// reminder: xpath array indexes start at 1
idAscXpathChecks[i + 1] = "//result/doc[" + (1 + i) + "]/float[@name='field(" + field + ")'][.='" + values.get(i) + "']";
idAscNegXpathChecks[i + 1] = "//result/doc[" + (1 + i) + "]/float[@name='product(-1," + field + ")'][.='"
+ (-1.0f * values.get(i)) + "']";
}
assertU(commit());
assertQ(req("q", "*:*", "fl", "id, " + field + ", field(" + field + ")", "rows", String.valueOf(numVals), "sort", "id asc"),
idAscXpathChecks);
assertQ(req("q", "*:*", "fl", "id, " + field + ", product(-1," + field + ")", "rows", String.valueOf(numVals), "sort", "id asc"),
idAscNegXpathChecks);
List<PosVal<Float>> ascNegPosVals
= toAscendingPosVals(values.stream().map(v -> -v).collect(Collectors.toList()), true);
String[] ascNegXpathChecks = new String[numVals + 1];
ascNegXpathChecks[0] = assertNumFound;
for (int i = 0 ; i < ascNegPosVals.size() ; ++i) {
PosVal<Float> posVal = ascNegPosVals.get(i);
ascNegXpathChecks[i + 1]
= "//result/doc[" + (1 + i) + "]/float[@name='" + field + "'][.='" + values.get(posVal.pos) + "']";
}
assertQ(req("q", "*:*", "fl", "id, " + field, "rows", String.valueOf(numVals), "sort", "product(-1," + field + ") asc"),
ascNegXpathChecks);
clearIndex();
assertU(commit());
}
private void doTestDoublePointFunctionQuery(String field) throws Exception {
assertTrue(h.getCore().getLatestSchema().getField(field).getType() instanceof PointField);
int numVals = 10 * RANDOM_MULTIPLIER;
// Restrict values to float range; otherwise conversion to float will cause truncation -> undefined results
List<Double> values = getRandomList(numVals, false, () -> {
Float f = Float.NaN;
while (f.isNaN()) {
f = Float.intBitsToFloat(random().nextInt());
}
return f.doubleValue();
});
String assertNumFound = "//*[@numFound='" + numVals + "']";
String[] idAscXpathChecks = new String[numVals + 1];
String[] idAscNegXpathChecks = new String[numVals + 1];
idAscXpathChecks[0] = assertNumFound;
idAscNegXpathChecks[0] = assertNumFound;
for (int i = 0 ; i < values.size() ; ++i) {
assertU(adoc("id", Character.valueOf((char)('A' + i)).toString(), field, String.valueOf(values.get(i))));
// reminder: xpath array indexes start at 1
idAscXpathChecks[i + 1] = "//result/doc[" + (1 + i) + "]/double[@name='field(" + field + ")'][.='" + values.get(i) + "']";
idAscNegXpathChecks[i + 1] = "//result/doc[" + (1 + i) + "]/float[@name='product(-1," + field + ")'][.='"
+ (-1.0f * values.get(i).floatValue()) + "']";
}
assertU(commit());
assertQ(req("q", "*:*", "fl", "id, " + field + ", field(" + field + ")", "rows", String.valueOf(numVals), "sort", "id asc"),
idAscXpathChecks);
assertQ(req("q", "*:*", "fl", "id, " + field + ", product(-1," + field + ")", "rows", String.valueOf(numVals), "sort", "id asc"),
idAscNegXpathChecks);
// Intentionally use floats here to mimic server-side function sorting
List<PosVal<Float>> ascNegPosVals
= toAscendingPosVals(values.stream().map(v -> -v.floatValue()).collect(Collectors.toList()), true);
String[] ascNegXpathChecks = new String[numVals + 1];
ascNegXpathChecks[0] = assertNumFound;
for (int i = 0 ; i < ascNegPosVals.size() ; ++i) {
PosVal<Float> posVal = ascNegPosVals.get(i);
ascNegXpathChecks[i + 1]
= "//result/doc[" + (1 + i) + "]/double[@name='" + field + "'][.='" + values.get(posVal.pos) + "']";
}
assertQ(req("q", "*:*", "fl", "id, " + field, "rows", String.valueOf(numVals), "sort", "product(-1," + field + ") asc"),
ascNegXpathChecks);
clearIndex();
assertU(commit());
}
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(values[i].replaceAll("(:|^[-+])", "\\\\$1")).append(' ');
} else {
builder.append(String.valueOf(values[i]).replace("-", "\\-")).append(' ');
}
}
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 field 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 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());
String[] arr = toAscendingStringArray(getRandomInstants(100, false), 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] + "\"", "fl", "id, " + fieldName),
"//*[@numFound='1']");
}
}
private void doTestDatePointFunctionQuery(String field) {
// This method is intentionally not randomized, because sorting by function happens
// at float precision, which causes ms(date) to give the same value for different dates.
// See https://issues.apache.org/jira/browse/SOLR-11825
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 + "," + baseDate +")) 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 doTestDatePointStats(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='" + (dates.length + 1) + "']",
"//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 doTestDatePointFieldsAtomicUpdates(String field) throws Exception {
long millis1 = random().nextLong() % MAX_DATE_EPOCH_MILLIS;
long millis2;
DateGapCeiling gap;
for ( ; ; ) {
millis2 = random().nextLong() % MAX_DATE_EPOCH_MILLIS;
gap = new DateGapCeiling(millis2 - millis1);
millis2 = gap.addTo(millis1); // adjust millis2 to the closest +/-UNIT gap
break;
}
String date1 = Instant.ofEpochMilli(millis1).toString();
String date2 = Instant.ofEpochMilli(millis2).toString();
assertU(adoc(sdoc("id", "1", field, date1)));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/date[@name='" + field + "'][.='" + date1 + "']");
assertU(adoc(sdoc("id", "1", field, ImmutableMap.of("set", date1 + gap.toString()))));
assertU(commit());
assertQ(req("q", "id:1"),
"//result/doc[1]/date[@name='" + field + "'][.='" + date2 + "']");
}
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());
SchemaField sf = h.getCore().getLatestSchema().getField(field);
boolean ignoredField = !(sf.indexed() || sf.stored() || sf.hasDocValues());
h.getCore().withSearcher(searcher -> {
DirectoryReader 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));
}
}
}
return null;
});
clearIndex();
assertU(commit());
}
public void testNonReturnable() throws Exception {
String[] ints = toStringArray(getRandomInts(2, false));
doTestReturnNonStored("foo_p_i_ni_ns", false, ints[0]);
doTestReturnNonStored("foo_p_i_ni_dv_ns", true, ints[0]);
doTestReturnNonStored("foo_p_i_ni_ns_mv", false, ints);
doTestReturnNonStored("foo_p_i_ni_dv_ns_mv", true, ints);
String[] longs = toStringArray(getRandomLongs(2, false));
doTestReturnNonStored("foo_p_l_ni_ns", false, longs[0]);
doTestReturnNonStored("foo_p_l_ni_dv_ns", true, longs[0]);
doTestReturnNonStored("foo_p_l_ni_ns_mv", false, longs);
doTestReturnNonStored("foo_p_l_ni_dv_ns_mv", true, longs);
String[] floats = toStringArray(getRandomFloats(2, false));
doTestReturnNonStored("foo_p_f_ni_ns", false, floats[0]);
doTestReturnNonStored("foo_p_f_ni_dv_ns", true, floats[0]);
doTestReturnNonStored("foo_p_f_ni_ns_mv", false, floats);
doTestReturnNonStored("foo_p_f_ni_dv_ns_mv", true, floats);
String[] doubles = toStringArray(getRandomDoubles(2, false));
doTestReturnNonStored("foo_p_d_ni_ns", false, doubles[0]);
doTestReturnNonStored("foo_p_d_ni_dv_ns", true, doubles[0]);
doTestReturnNonStored("foo_p_d_ni_ns_mv", false, doubles);
doTestReturnNonStored("foo_p_d_ni_dv_ns_mv", true, doubles);
String[] dates = new String[] { getRandomDateMaybeWithMath(), getRandomDateMaybeWithMath() };
doTestReturnNonStored("foo_p_dt_ni_ns", false, dates[0]);
doTestReturnNonStored("foo_p_dt_ni_dv_ns", true, dates[0]);
doTestReturnNonStored("foo_p_dt_ni_ns_mv", false, dates);
doTestReturnNonStored("foo_p_dt_ni_dv_ns_mv", true, dates);
}
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"};
@SuppressWarnings({"rawtypes"})
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:FIELD_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 equivalent 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;
}
}