blob: a581264c92b57468d2de56151cfc0222bd5f1e57 [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.request;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.apache.lucene.util.BytesRef;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.IntervalFacets.FacetInterval;
import org.apache.solr.request.IntervalFacets.IntervalCompareResult;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.NumberType;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.StrField;
import org.apache.solr.search.SyntaxError;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestIntervalFaceting extends SolrTestCaseJ4 {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final static long DATE_START_TIME_RANDOM_TEST = 1499797224224L;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT);
@BeforeClass
public static void beforeTests() throws Exception {
// we need DVs on point fields to compute stats & facets
if (Boolean.getBoolean(NUMERIC_POINTS_SYSPROP)) System.setProperty(NUMERIC_DOCVALUES_SYSPROP,"true");
initCore("solrconfig-basic.xml", "schema-docValuesFaceting.xml");
}
@Override
public void tearDown() throws Exception {
assertU(delQ("*:*"));
assertU(commit());
assertU(optimize());
assertQ(req("*:*"), "//*[@numFound='0']");
super.tearDown();
}
@Test
public void testMultiValueFields() {
assertU(adoc("id", "1", "test_ss_dv", "dog"));
assertU(adoc("id", "2", "test_ss_dv", "cat"));
assertU(adoc("id", "3", "test_ss_dv", "bird"));
assertU(adoc("id", "4", "test_ss_dv", "turtle"));
assertU(commit());
assertU(adoc("id", "5", "test_ss_dv", "\\goodbye,"));
assertU(adoc("id", "6", "test_ss_dv", ",hello\\"));
assertU(adoc("id", "7", "test_ss_dv", "dog"));
assertU(commit());
assertU(adoc("id", "8", "test_ss_dv", "dog"));
assertU(adoc("id", "9", "test_ss_dv", "cat"));
assertU(adoc("id", "10"));
assertU(commit());
assertIntervalQueriesString("test_ss_dv");
assertU(delQ("*:*"));
assertU(commit());
assertU(optimize());
assertU(adoc("id", "1", "test_ss_dv", "dog", "test_ss_dv", "cat"));
assertU(adoc("id", "2", "test_ss_dv", "cat", "test_ss_dv", "bird"));
assertU(commit());
assertIntervalQuery("test_ss_dv", "[hello,hello]", "0");
assertIntervalQuery("test_ss_dv", "[dog,dog]", "1");
assertIntervalQuery("test_ss_dv", "[cat,cat]", "2");
assertIntervalQuery("test_ss_dv", "[*,*]", "2", "[*,cat)", "1", "[cat,dog)", "2", "[dog,*)", "1");
}
@Test
public void testMultipleSegments() {
assertU(adoc("id", "1", "test_s_dv", "dog"));
assertU(adoc("id", "2", "test_s_dv", "cat"));
assertU(adoc("id", "3", "test_s_dv", "bird"));
assertU(adoc("id", "4", "test_s_dv", "turtle"));
assertU(commit());
assertU(adoc("id", "5", "test_s_dv", "\\goodbye,"));
assertU(adoc("id", "6", "test_s_dv", ",hello\\"));
assertU(adoc("id", "7", "test_s_dv", "dog"));
assertU(commit());
assertU(adoc("id", "8", "test_s_dv", "dog"));
assertU(adoc("id", "9", "test_s_dv", "cat"));
assertU(adoc("id", "10"));
assertU(commit());
int i = 11;
while (getNumberOfReaders() < 2 && i < 20) {
//try to get more than one segment
assertU(adoc("id", String.valueOf(i), "test_i_dv", String.valueOf(i)));
assertU(commit());
i++;
}
if (getNumberOfReaders() < 2) {
// It is OK if for some seeds we fall into this case (for example, TieredMergePolicy with
// segmentsPerTier=2). Most of the case we shouldn't and the test should proceed.
log.warn("Could not generate more than 1 segment for this seed. Will skip the test");
return;
}
assertIntervalQueriesString("test_s_dv");
}
@Test
public void testMultipleTerms() {
assertU(adoc("id", "1", "test_s_dv", "Buenos Aires"));
assertU(adoc("id", "2", "test_s_dv", "New York"));
assertU(adoc("id", "3", "test_s_dv", "Los Angeles"));
assertU(adoc("id", "4", "test_s_dv", "San Francisco"));
assertU(adoc("id", "5", "test_s_dv", "Las Vegas"));
assertU(adoc("id", "6", "test_s_dv", "São Paulo"));
assertU(adoc("id", "10"));
assertU(commit());
assertIntervalQuery("test_s_dv", "[*,*]", "6");
assertIntervalQuery("test_s_dv", "[A,C]", "1");
assertIntervalQuery("test_s_dv", "[Buenos Aires,Buenos Aires]", "1");
assertIntervalQuery("test_s_dv", "[Las,Los]", "1");
assertIntervalQuery("test_s_dv", "[Las,Los Angeles]", "2");
assertIntervalQuery("test_s_dv", "[Las,Los Angeles)", "1");
assertIntervalQuery("test_s_dv", "[Las\\,,Los Angeles]", "1");
}
private int getNumberOfReaders() {
try {
return h.getCore().withSearcher(searcher -> searcher.getTopReaderContext().leaves().size());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
public void testBasic() {
assertU(adoc("id", "1", "test_s_dv", "dog"));
assertU(adoc("id", "2", "test_s_dv", "cat"));
assertU(adoc("id", "3", "test_s_dv", "bird"));
assertU(adoc("id", "4", "test_s_dv", "turtle"));
assertU(adoc("id", "5", "test_s_dv", "\\goodbye,"));
assertU(adoc("id", "6", "test_s_dv", ",hello\\"));
assertU(adoc("id", "7", "test_s_dv", "dog"));
assertU(adoc("id", "8", "test_s_dv", "dog"));
assertU(adoc("id", "9", "test_s_dv", "cat"));
assertU(adoc("id", "10"));
assertU(commit());
assertIntervalQueriesString("test_s_dv");
// error cases
assertQEx("missing beginning of range",
req("fl", "test_s_dv", "q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "bird,bird]"),
SolrException.ErrorCode.BAD_REQUEST
);
assertQEx("only separator is escaped",
req("fl", "test_s_dv", "q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "(bird\\,turtle]"),
SolrException.ErrorCode.BAD_REQUEST
);
assertQEx("missing separator",
req("fl", "test_s_dv", "q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "(bird]"),
SolrException.ErrorCode.BAD_REQUEST
);
assertQEx("missing end of range",
req("fl", "test_s_dv", "q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "(bird,turtle"),
SolrException.ErrorCode.BAD_REQUEST
);
}
@Test
public void testMultipleFields() {
assertU(adoc("id", "1", "test_s_dv", "dog", "test_l_dv", "1"));
assertU(adoc("id", "2", "test_s_dv", "cat", "test_l_dv", "2"));
assertU(adoc("id", "3", "test_s_dv", "bird", "test_l_dv", "3"));
assertU(adoc("id", "4", "test_s_dv", "turtle", "test_l_dv", "4"));
assertU(adoc("id", "5", "test_s_dv", "\\goodbye,", "test_l_dv", "5"));
assertU(adoc("id", "6", "test_s_dv", ",hello\\", "test_l_dv", "6"));
assertU(adoc("id", "7", "test_s_dv", "dog", "test_l_dv", "7"));
assertU(adoc("id", "8", "test_s_dv", "dog", "test_l_dv", "8"));
assertU(adoc("id", "9", "test_s_dv", "cat", "test_l_dv", "9"));
assertU(adoc("id", "10"));
assertU(commit());
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"facet.interval", "test_l_dv", "f.test_s_dv.facet.interval.set", "[cat,dog]",
"f.test_l_dv.facet.interval.set", "[3,6]",
"f.test_l_dv.facet.interval.set", "[5,9]"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[cat,dog]'][.=5]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[3,6]'][.=4]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[5,9]'][.=5]");
}
@Test
public void testWithFieldCache() {
assertU(adoc("id", "1", "test_s", "dog", "test_l", "1"));
assertU(adoc("id", "2", "test_s", "cat", "test_l", "2"));
assertU(adoc("id", "3", "test_s", "bird", "test_l", "3"));
assertU(adoc("id", "4", "test_s", "turtle", "test_l", "4"));
assertU(adoc("id", "5", "test_s", "\\goodbye,", "test_l", "5"));
assertU(adoc("id", "6", "test_s", ",hello\\", "test_l", "6"));
assertU(adoc("id", "7", "test_s", "dog", "test_l", "7"));
assertU(adoc("id", "8", "test_s", "dog", "test_l", "8"));
assertU(adoc("id", "9", "test_s", "cat", "test_l", "9"));
assertU(adoc("id", "10"));
assertU(commit());
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s",
"facet.interval", "test_l", "f.test_s.facet.interval.set", "[cat,dog]",
"f.test_l.facet.interval.set", "[3,6]",
"f.test_l.facet.interval.set", "[5,9]"),
"//lst[@name='facet_intervals']/lst[@name='test_s']/int[@name='[cat,dog]'][.=5]",
"//lst[@name='facet_intervals']/lst[@name='test_l']/int[@name='[3,6]'][.=4]",
"//lst[@name='facet_intervals']/lst[@name='test_l']/int[@name='[5,9]'][.=5]");
}
@Test
@Slow
public void testRandom() throws Exception {
// All field values will be a number between 0 and cardinality
int cardinality = 10000;
// Fields to use for interval faceting
String[] fields = new String[]{
"test_s_dv", "test_i_dv", "test_l_dv", "test_f_dv", "test_d_dv", "test_dt_dv",
"test_ss_dv", "test_is_dv", "test_fs_dv", "test_ls_dv", "test_ds_dv", "test_dts_dv", "test_s", "test_i",
"test_l", "test_f", "test_d", "test_dt", "test_ss", "test_is", "test_fs", "test_ls", "test_ds", "test_dts",
"test_i_p", "test_is_p", "test_l_p", "test_ls_p", "test_f_p", "test_fs_p", "test_d_p", "test_ds_p", "test_dts_p"
};
for (int i = 0; i < atLeast(500); i++) {
if (random().nextInt(50) == 0) {
//have some empty docs
assertU(adoc("id", String.valueOf(i)));
continue;
}
if (random().nextInt(100) == 0 && i > 0) {
//delete some docs
assertU(delI(String.valueOf(i - 1)));
}
String[] docFields = new String[(random().nextInt(5)) * 12 + 14];
docFields[0] = "id";
docFields[1] = String.valueOf(i * (random().nextBoolean()?1:-1)); // in the queries we do positive and negative
docFields[2] = "test_s";
docFields[3] = String.valueOf(randomInt(cardinality));
docFields[4] = "test_i";
docFields[5] = String.valueOf(randomInt(cardinality));
docFields[6] = "test_l";
docFields[7] = String.valueOf(randomLong(cardinality));
docFields[8] = "test_f";
docFields[9] = String.valueOf(randomFloat(cardinality));
docFields[10] = "test_d";
docFields[11] = String.valueOf(raondomDouble(cardinality));
docFields[12] = "test_dt";
docFields[13] = dateFormat.format(new Date(randomMs(cardinality)));
for (int j = 14; j < docFields.length; ) {
docFields[j++] = "test_ss";
docFields[j++] = String.valueOf(randomInt(cardinality));
docFields[j++] = "test_is";
docFields[j++] = String.valueOf(randomInt(cardinality));
docFields[j++] = "test_ls";
docFields[j++] = String.valueOf(randomLong(cardinality));
docFields[j++] = "test_fs";
docFields[j++] = String.valueOf(randomFloat(cardinality));
docFields[j++] = "test_ds";
docFields[j++] = String.valueOf(raondomDouble(cardinality));
docFields[j++] = "test_dts";
docFields[j++] = dateFormat.format(new Date(randomMs(cardinality)));
}
assertU(adoc(docFields));
if (random().nextInt(50) == 0) {
assertU(commit());
}
}
assertU(commit());
for (int i = 0; i < atLeast(10000); i++) {
doTestQuery(cardinality, fields);
}
}
long randomMs(int cardinality) {
return DATE_START_TIME_RANDOM_TEST + random().nextInt(cardinality) * 1000 * (random().nextBoolean()?1:-1);
}
double raondomDouble(int cardinality) {
if (rarely()) {
int num = random().nextInt(4);
if (num == 0) return Double.NEGATIVE_INFINITY;
if (num == 1) return Double.POSITIVE_INFINITY;
if (num == 2) return Double.MIN_VALUE;
if (num == 3) return Double.MAX_VALUE;
}
Double d = Double.NaN;
while (d.isNaN()) {
d = random().nextDouble();
}
return d * cardinality * (random().nextBoolean()?1:-1);
}
float randomFloat(int cardinality) {
if (rarely()) {
int num = random().nextInt(4);
if (num == 0) return Float.NEGATIVE_INFINITY;
if (num == 1) return Float.POSITIVE_INFINITY;
if (num == 2) return Float.MIN_VALUE;
if (num == 3) return Float.MAX_VALUE;
}
Float f = Float.NaN;
while (f.isNaN()) {
f = random().nextFloat();
}
return f * cardinality * (random().nextBoolean()?1:-1);
}
int randomInt(int cardinality) {
if (rarely()) {
int num = random().nextInt(2);
if (num == 0) return Integer.MAX_VALUE;
if (num == 1) return Integer.MIN_VALUE;
}
return random().nextInt(cardinality) * (random().nextBoolean()?1:-1);
}
long randomLong(int cardinality) {
if (rarely()) {
int num = random().nextInt(2);
if (num == 0) return Long.MAX_VALUE;
if (num == 1) return Long.MIN_VALUE;
}
return randomInt(cardinality);
}
/**
* Executes one query using interval faceting and compares with the same query using
* facet query with the same range
*/
@SuppressWarnings("unchecked")
private void doTestQuery(int cardinality, String[] fields) throws Exception {
String[] startOptions = new String[]{"(", "["};
String[] endOptions = new String[]{")", "]"};
ModifiableSolrParams params = new ModifiableSolrParams();
if (rarely()) {
params.set("q", "*:*");
} else {
// the query should match some documents in most cases
String[] qRange = getRandomRange(cardinality, "id");
params.set("q", "id:[" + qRange[0] + " TO " + qRange[1] + "]");
}
params.set("facet", "true");
String field = pickRandom(fields); //choose from any of the fields
params.set("facet.interval", field);
// number of intervals
for (int i = 0; i < 1 + random().nextInt(20); i++) {
String[] interval = getRandomRange(cardinality, field);
String open = pickRandom(startOptions);
String close = pickRandom(endOptions);
params.add("f." + field + ".facet.interval.set", open + interval[0] + "," + interval[1] + close);
params.add("facet.query", field + ":" + open.replace('(', '{') + interval[0] + " TO " + interval[1] + close.replace(')', '}'));
}
SolrQueryRequest req = req(params);
try {
SolrQueryResponse rsp = h.queryAndResponse("", req);
NamedList<Object> facetQueries = (NamedList<Object>) ((NamedList<Object>) rsp.getValues().get("facet_counts")).get("facet_queries");
NamedList<Object> facetIntervals = (NamedList<Object>) ((NamedList<Object>) ((NamedList<Object>) rsp.getValues().get("facet_counts"))
.get("facet_intervals")).get(field);
assertEquals("Responses don't have the same number of facets: \n" + facetQueries + "\n" + facetIntervals,
facetQueries.size(), getCountDistinctIntervals(facetIntervals));
for (int i = 0; i < facetIntervals.size(); i++) {
assertEquals("Interval did not match: " + field + ": " + facetIntervals.getName(i) + "\nResponse: " + rsp.getValues().get("facet_counts"),
facetQueries.get(field + ":" + facetIntervals.getName(i).replace(",", " TO ").replace('(', '{').replace(')', '}')).toString(),
facetIntervals.getVal(i).toString());
}
} finally {
req.close();
}
}
private int getCountDistinctIntervals(NamedList<Object> facetIntervals) {
Set<String> distinctIntervals = new HashSet<>(facetIntervals.size());
for (int i = 0; i < facetIntervals.size(); i++) {
distinctIntervals.add(facetIntervals.getName(i));
}
return distinctIntervals.size();
}
/**
* Returns a random range. It's guaranteed that the first
* number will be lower than the second. The range could have values greater than "max",
* for example [Integer/Long/Float/Double].[MIN/MAX_VALUE,POSITIVE/NEGATIVE_INFINITY]
* If the fieldName is "test_s_dv" or "test_ss_dv" (the
* two fields used for Strings), the comparison will be done
* alphabetically
* If the field is a Date, a date range will be returned
* The range could also contain "*" as beginning and/or end of the range
*/
private String[] getRandomRange(int max, String fieldName) {
Number[] values = new Number[2];
FieldType ft = h.getCore().getLatestSchema().getField(fieldName).getType();
if (ft.getNumberType() == null) {
assert ft instanceof StrField;
values[0] = randomInt(max);
values[1] = randomInt(max);
Arrays.sort(values, (o1, o2) -> String.valueOf(o1).compareTo(String.valueOf(o2)));
} else {
switch (ft.getNumberType()) {
case DOUBLE:
values[0] = raondomDouble(max);
values[1] = raondomDouble(max);
break;
case FLOAT:
values[0] = randomFloat(max);
values[1] = randomFloat(max);
break;
case INTEGER:
values[0] = randomInt(max);
values[1] = randomInt(max);
break;
case LONG:
values[0] = randomLong(max);
values[1] = randomLong(max);
break;
case DATE:
values[0] = randomMs(max);
values[1] = randomMs(max);
break;
default:
throw new AssertionError("Unexpected number type");
}
Arrays.sort(values);
}
String[] stringValues = new String[2];
if (rarely()) {
stringValues[0] = "*";
} else {
if (ft.getNumberType() == NumberType.DATE) {
stringValues[0] = dateFormat.format(values[0]);
} else {
stringValues[0] = String.valueOf(values[0]);
}
}
if (rarely()) {
stringValues[1] = "*";
} else {
if (ft.getNumberType() == NumberType.DATE) {
stringValues[1] = dateFormat.format(values[1]);
} else {
stringValues[1] = String.valueOf(values[1]);
}
}
return stringValues;
}
@Test
public void testParse() throws SyntaxError {
assertInterval("test_l_dv", "(0,2)", new long[]{1}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "(0,2]", new long[]{1, 2}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "[0,2]", new long[]{0, 1, 2}, new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "[0,2)", new long[]{0, 1}, new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "(0,*)", new long[]{1, 2, 3, Integer.MAX_VALUE, Long.MAX_VALUE}, new long[]{-1, 0, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{});
assertInterval("test_l_dv", "(*,2)", new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE, 0, 1}, new long[]{}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "(*,*)", new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE, 0, 1, 2, 3, Integer.MAX_VALUE, Long.MAX_VALUE}, new long[]{}, new long[]{});
assertInterval("test_l_dv", "[0,*]", new long[]{0, 1, 2, 3, Integer.MAX_VALUE, Long.MAX_VALUE}, new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{});
assertInterval("test_l_dv", "[*,2]", new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE, 0, 1, 2}, new long[]{}, new long[]{3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "[*,*]", new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE, 0, 1, 2, 3, Integer.MAX_VALUE, Long.MAX_VALUE}, new long[]{}, new long[]{});
assertInterval("test_l_dv", "(2,2)", new long[]{}, new long[]{2, 1, 0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "(0,0)", new long[]{}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{1, 2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "(0," + Long.MAX_VALUE + "]", new long[]{1, 2, 3, Integer.MAX_VALUE, Long.MAX_VALUE}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{});
assertInterval("test_l_dv", "(0," + Long.MAX_VALUE + ")", new long[]{1, 2, 3, Integer.MAX_VALUE}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{Long.MAX_VALUE});
assertInterval("test_l_dv", "(" + Long.MIN_VALUE + ",0)", new long[]{-1, Integer.MIN_VALUE}, new long[]{Long.MIN_VALUE}, new long[]{1, 2, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "[" + Long.MIN_VALUE + ",0)", new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{}, new long[]{1, 2, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "[" + Long.MIN_VALUE + "," + Long.MAX_VALUE + "]", new long[]{-1, Integer.MIN_VALUE, Long.MIN_VALUE, 1, 2, Integer.MAX_VALUE, Long.MAX_VALUE}, new long[]{}, new long[]{});
assertInterval("test_l_dv", "(" + Long.MIN_VALUE + "," + Long.MAX_VALUE + ")", new long[]{-1, Integer.MIN_VALUE, 1, 2, Integer.MAX_VALUE}, new long[]{Long.MIN_VALUE}, new long[]{Long.MAX_VALUE});
assertInterval("test_l_dv", "( 0,2)", new long[]{1}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "( 0,2)", new long[]{1}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "(0, 2)", new long[]{1}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "( 0 , 2 )", new long[]{1}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", " ( 0 , 2 ) ", new long[]{1}, new long[]{0, -1, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertInterval("test_l_dv", "[-1,1]", new long[]{-1, 0, 1}, new long[]{-2, Integer.MIN_VALUE, Long.MIN_VALUE}, new long[]{2, 3, Integer.MAX_VALUE, Long.MAX_VALUE});
assertStringInterval("test_s_dv", "[A,B]", "A", "B");
assertStringInterval("test_s_dv", "[A,b]", "A", "b");
assertStringInterval("test_s_dv", "[A\\,,B]", "A,", "B");
assertStringInterval("test_s_dv", "[A\\),B]", "A)", "B");
assertStringInterval("test_s_dv", "['A',B]", "'A'", "B");
assertStringInterval("test_s_dv", "[\"A\",B]", "\"A\"", "B");
assertStringInterval("test_s_dv", "[A B C,B]", "A B C", "B");
assertStringInterval("test_s_dv", "[ A B C ,B]", "A B C", "B");
// These two are currently not possible
// assertStringInterval("test_s_dv", "[\\ A B C ,B]", " A B C", "B");
// assertStringInterval("test_s_dv", "[\\*,B]", "*", "B");
//invalid intervals
assertBadInterval("test_l_dv", "0,2)", "Invalid start character");
assertBadInterval("test_l_dv", "{0,2)", "Invalid start character");
assertBadInterval("test_l_dv", "(0,2", "Invalid end character");
assertBadInterval("test_l_dv", "(0,2}", "Invalid end character");
assertBadInterval("test_l_dv", "(0, )", "Empty interval limit");
assertBadInterval("test_l_dv", "(0)", "Missing unescaped comma separating interval");
assertBadInterval("test_l_dv", "(,0)", "Empty interval limit");
assertBadInterval("test_l_dv", "(0 2)", "Missing unescaped comma separating interval");
assertBadInterval("test_l_dv", "(0 TO 2)", "Missing unescaped comma separating interval");
assertBadInterval("test_l_dv", "(0 \\, 2)", "Missing unescaped comma separating interval");
assertBadInterval("test_l_dv", "(A, 2)", "Invalid start interval for key");
assertBadInterval("test_l_dv", "(2, A)", "Invalid end interval for key");
assertBadInterval("test_l_dv", "(0,)", "Empty interval limit");
assertBadInterval("test_l_dv", "(0,-1)", "Start is higher than end in interval for key");
assertBadInterval("test_s_dv", "A,B)", "Invalid start character");
assertBadInterval("test_s_dv", "(B,A)", "Start is higher than end in interval for key");
assertBadInterval("test_s_dv", "(a,B)", "Start is higher than end in interval for key");
assertIntervalKey("test_s_dv", "[A,B]", "[A,B]");
assertIntervalKey("test_s_dv", "(A,*]", "(A,*]");
assertIntervalKey("test_s_dv", "{!}(A,*]", "(A,*]");
assertIntervalKey("test_s_dv", "{!key=foo}(A,*]", "foo");
assertIntervalKey("test_s_dv", "{!key='foo'}(A,*]", "foo");
assertIntervalKey("test_s_dv", "{!key='foo bar'}(A,*]", "foo bar");
assertIntervalKey("test_s_dv", "{!key='foo' bar}(A,*]", "foo");
assertIntervalKey("test_s_dv", "{!key=$i}(A,*]", "foo", "i", "foo");
assertIntervalKey("test_s_dv", "{!key=$i}(A,*]", "foo bar", "i", "foo bar");
assertIntervalKey("test_s_dv", "{!key=$i}(A,*]", "'foo'", "i", "'foo'");
assertIntervalKey("test_s_dv", "{!key=$i}(A,*]", "\"foo\"", "i", "\"foo\"");
assertIntervalKey("test_s_dv", "{!key='[A,B]'}(A,B)", "[A,B]");
assertIntervalKey("test_s_dv", "{!key='\\{\\{\\{'}(A,B)", "{{{");
assertIntervalKey("test_s_dv", "{!key='\\{A,B\\}'}(A,B)", "{A,B}");
assertIntervalKey("test_s_dv", "{!key='\"A,B\"'}(A,B)", "\"A,B\"");
assertIntervalKey("test_s_dv", "{!key='A..B'}(A,B)", "A..B");
assertIntervalKey("test_s_dv", "{!key='A TO B'}(A,B)", "A TO B");
assertU(adoc("id", "1", "test_s_dv", "dog", "test_l_dv", "1"));
assertU(adoc("id", "2", "test_s_dv", "cat", "test_l_dv", "2"));
assertU(adoc("id", "3", "test_s_dv", "bird", "test_l_dv", "3"));
assertU(adoc("id", "4", "test_s_dv", "turtle", "test_l_dv", "4"));
assertU(adoc("id", "5", "test_s_dv", "\\goodbye,", "test_l_dv", "5"));
assertU(adoc("id", "6", "test_s_dv", ",hello\\", "test_l_dv", "6"));
assertU(adoc("id", "7", "test_s_dv", "dog", "test_l_dv", "7"));
assertU(adoc("id", "8", "test_s_dv", "dog", "test_l_dv", "8"));
assertU(adoc("id", "9", "test_s_dv", "cat", "test_l_dv", "9"));
assertU(adoc("id", "10"));
assertU(commit());
// facet.interval not set
assertQ(req("q", "*:*", "facet", "true",
"f.test_s_dv.facet.interval.set", "[cat,dog]",
"f.test_l_dv.facet.interval.set", "[3,6]",
"f.test_l_dv.facet.interval.set", "[5,9]"),
"count(//lst[@name='facet_intervals']/lst)=0");
// facet.interval only on one of the fields
assertQ(req("q", "*:*", "facet", "true",
"facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "[cat,dog]",
"f.test_l_dv.facet.interval.set", "[3,6]",
"f.test_l_dv.facet.interval.set", "[5,9]"),
"count(//lst[@name='facet_intervals']/lst)=1",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[cat,dog]'][.=5]");
// existing fields in facet.interval with no intervals defined
assertQEx("Unexpected exception",
"Missing required parameter: f.test_l_dv.facet.interval.set (or default: facet.interval.set)",
req("q", "*:*", "facet", "true",
"facet.interval", "test_s_dv",
"facet.interval", "test_l_dv",
"f.test_s_dv.facet.interval.set", "[cat,dog]"),
SolrException.ErrorCode.BAD_REQUEST);
// use of facet.interval.set
assertQ(req("q", "*:*", "facet", "true",
"facet.interval", "test_s_dv",
"facet.interval", "test_l_dv",
"facet.interval.set", "[1,2]"),
"count(//lst[@name='facet_intervals']/lst)=2",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[1,2]'][.=0]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[1,2]'][.=2]"
);
// multiple facet.interval.set
assertQ(req("q", "*:*", "facet", "true",
"facet.interval", "test_s_dv",
"facet.interval", "test_l_dv",
"facet.interval.set", "[1,2]",
"facet.interval.set", "[2,3]",
"facet.interval.set", "[3,4]"),
"count(//lst[@name='facet_intervals']/lst)=2",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[1,2]'][.=0]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[2,3]'][.=0]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[3,4]'][.=0]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[1,2]'][.=2]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[2,3]'][.=2]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[3,4]'][.=2]"
);
// use of facet.interval.set and override
assertQ(req("q", "*:*", "facet", "true",
"facet.interval", "test_s_dv",
"facet.interval", "test_l_dv",
"facet.interval.set", "[1,2]",
"f.test_l_dv.facet.interval.set", "[3,4]",
"f.test_l_dv.facet.interval.set", "[4,5]"),
"count(//lst[@name='facet_intervals']/lst)=2",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[1,2]'][.=0]",
"count(//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int)=2", // interval [1,2] not present
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[3,4]'][.=2]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[4,5]'][.=2]"
);
assertQ(req("q", "*:*", "facet", "true",
"facet.interval", "test_s_dv",
"facet.interval", "test_l_dv",
"facet.interval.set", "[1,2]",
"facet.interval.set", "[2,3]",
"facet.interval.set", "[3,4]",
"f.test_s_dv.facet.interval.set", "[cat,dog]"),
"count(//lst[@name='facet_intervals']/lst)=2",
"count(//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int)=1", // only [cat,dog] in test_s_dv
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[cat,dog]'][.=5]",
"count(//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int)=3",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[1,2]'][.=2]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[2,3]'][.=2]",
"//lst[@name='facet_intervals']/lst[@name='test_l_dv']/int[@name='[3,4]'][.=2]"
);
// use of facet.interval.set with wrong field type
assertQEx("Unexpected Exception",
"Invalid start interval",
req("q", "*:*", "facet", "true",
"facet.interval", "test_l_dv",
"f.test_l_dv.facet.interval.set", "[cat,dog]"),
SolrException.ErrorCode.BAD_REQUEST);
}
private void assertStringInterval(String fieldName, String intervalStr,
String expectedStart, String expectedEnd) throws SyntaxError {
SchemaField f = h.getCore().getLatestSchema().getField(fieldName);
FacetInterval interval = new FacetInterval(f, intervalStr, new ModifiableSolrParams());
assertEquals("Expected start " + expectedStart + " but found " + f.getType().toObject(f, interval.start),
interval.start, new BytesRef(f.getType().toInternal(expectedStart)));
assertEquals("Expected end " + expectedEnd + " but found " + f.getType().toObject(f, interval.end),
interval.end, new BytesRef(f.getType().toInternal(expectedEnd)));
}
private void assertBadInterval(String fieldName, String intervalStr, String errorMsg) {
SchemaField f = h.getCore().getLatestSchema().getField(fieldName);
SyntaxError e = expectThrows(SyntaxError.class, () -> new FacetInterval(f, intervalStr, new ModifiableSolrParams()));
assertTrue("Unexpected error message for interval String: " + intervalStr + ": " +
e.getMessage(), e.getMessage().contains(errorMsg));
}
private void assertInterval(String fieldName, String intervalStr, long[] included, long[] lowerThanStart, long[] graterThanEnd) throws SyntaxError {
SchemaField f = h.getCore().getLatestSchema().getField(fieldName);
FacetInterval interval = new FacetInterval(f, intervalStr, new ModifiableSolrParams());
for (long l : included) {
assertEquals("Value " + l + " should be INCLUDED for interval" + interval,
IntervalCompareResult.INCLUDED, interval.includes(l));
}
for (long l : lowerThanStart) {
assertEquals("Value " + l + " should be LOWER_THAN_START for inteval " + interval,
IntervalCompareResult.LOWER_THAN_START, interval.includes(l));
}
for (long l : graterThanEnd) {
assertEquals("Value " + l + " should be GRATER_THAN_END for inteval " + interval,
IntervalCompareResult.GREATER_THAN_END, interval.includes(l));
}
}
private void assertIntervalKey(String fieldName, String intervalStr,
String expectedKey, String...params) throws SyntaxError {
assert (params.length&1)==0:"Params must have an even number of elements";
SchemaField f = h.getCore().getLatestSchema().getField(fieldName);
ModifiableSolrParams solrParams = new ModifiableSolrParams();
for (int i = 0; i < params.length - 1;) {
solrParams.set(params[i], params[i+1]);
i+=2;
}
FacetInterval interval = new FacetInterval(f, intervalStr, solrParams);
assertEquals("Expected key " + expectedKey + " but found " + interval.getKey(),
expectedKey, interval.getKey());
}
public void testChangeKey() {
assertU(adoc("id", "1", "test_s_dv", "dog"));
assertU(adoc("id", "2", "test_s_dv", "cat"));
assertU(adoc("id", "3", "test_s_dv", "bird"));
assertU(adoc("id", "4", "test_s_dv", "cat"));
assertU(adoc("id", "5", "test_s_dv", "turtle"));
assertU(adoc("id", "6", "test_s_dv", "dog"));
assertU(adoc("id", "7", "test_s_dv", "dog"));
assertU(adoc("id", "8", "test_s_dv", "dog"));
assertU(adoc("id", "9", "test_s_dv", "cat"));
assertU(adoc("id", "10"));
assertU(commit());
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "{!key=foo}[bird,bird]",
"f.test_s_dv.facet.interval.set", "{!key='bar'}(bird,dog)"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='foo'][.=1]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='bar'][.=3]");
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "{!key=Birds}[bird,bird]",
"f.test_s_dv.facet.interval.set", "{!key='foo bar'}(bird,dog)"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='Birds'][.=1]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='foo bar'][.=3]");
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "{!key=$p}[bird,bird]",
"p", "foo bar"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='foo bar'][.=1]");
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "{!key='[bird,\\}'}[bird,*]",
"f.test_s_dv.facet.interval.set", "{!key='\\{bird,dog\\}'}(bird,dog)",
"f.test_s_dv.facet.interval.set", "{!key='foo'}(bird,dog})"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[bird,}'][.=9]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='{bird,dog}'][.=3]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='foo'][.=7]");
}
@Test
public void testLongFields() {
assertU(adoc("id", "1", "test_l_dv", "0"));
assertU(adoc("id", "2", "test_l_dv", "1"));
assertU(adoc("id", "3", "test_l_dv", "2"));
assertU(adoc("id", "4", "test_l_dv", "3"));
assertU(adoc("id", "5", "test_l_dv", "4"));
assertU(adoc("id", "6", "test_l_dv", "5"));
assertU(adoc("id", "7", "test_l_dv", "6"));
assertU(adoc("id", "8", "test_l_dv", "7"));
assertU(adoc("id", "9", "test_l_dv", "8"));
assertU(adoc("id", "10"));
assertU(adoc("id", "11", "test_l_dv", "10"));
assertU(commit());
assertIntervalQueriesNumeric("test_l_dv");
assertU(adoc("id", "12", "test_l_dv", String.valueOf(Long.MAX_VALUE - 3)));
assertU(adoc("id", "13", "test_l_dv", String.valueOf(Long.MAX_VALUE - 2)));
assertU(adoc("id", "14", "test_l_dv", String.valueOf(Long.MAX_VALUE - 1)));
assertU(adoc("id", "15", "test_l_dv", String.valueOf(Long.MAX_VALUE)));
assertU(adoc("id", "16", "test_l_dv", String.valueOf(Long.MIN_VALUE)));
assertU(commit());
assertIntervalQuery("test_l_dv", "[0," + Integer.MAX_VALUE + "]", "10");
assertIntervalQuery("test_l_dv", "(10," + Long.MAX_VALUE + "]", "4");
assertIntervalQuery("test_l_dv", "[" + Long.MAX_VALUE + "," + Long.MAX_VALUE + "]", "1");
assertIntervalQuery("test_l_dv", "[" + Long.MAX_VALUE + ",*]", "1");
assertIntervalQuery("test_l_dv", "(" + Long.MAX_VALUE + ",*]", "0");
assertIntervalQuery("test_l_dv", "(*, " + Long.MIN_VALUE + "]", "1");
assertIntervalQuery("test_l_dv", "(*, " + Long.MIN_VALUE + ")", "0");
assertIntervalQuery("test_l_dv", "(" + (Long.MAX_VALUE - 1) + ",*]", "1");
assertIntervalQuery("test_l_dv", "[" + (Long.MAX_VALUE - 1) + ",*]", "2");
}
@Test
public void testFloatFields() {
doTestFloat("test_f_dv", false);
}
private void doTestFloat(String field, boolean testDouble) {
assertU(adoc("id", "1", field, "0"));
assertU(adoc("id", "2", field, "1"));
assertU(adoc("id", "3", field, "2"));
assertU(adoc("id", "4", field, "3"));
assertU(adoc("id", "5", field, "4"));
assertU(adoc("id", "6", field, "5"));
assertU(adoc("id", "7", field, "6"));
assertU(adoc("id", "8", field, "7"));
assertU(adoc("id", "9", field, "8"));
assertU(adoc("id", "10"));
assertU(adoc("id", "11", field, "10"));
assertU(commit());
assertIntervalQueriesNumeric(field);
assertU(adoc("id", "12", field, "1.3"));
assertU(adoc("id", "13", field, "4.5"));
assertU(adoc("id", "14", field, "6.7"));
assertU(adoc("id", "15", field, "123.45"));
assertU(commit());
assertIntervalQuery(field, "[0," + Integer.MAX_VALUE + "]", "14");
assertIntervalQuery(field, "[0,1]", "2");
assertIntervalQuery(field, "[0,2]", "4");
assertIntervalQuery(field, "(1,2)", "1");
assertIntervalQuery(field, "(1,1)", "0");
assertIntervalQuery(field, "(4,7)", "4");
assertIntervalQuery(field, "(123,*)", "1");
clearIndex();
assertU(adoc("id", "16", field, "-1.3"));
assertU(adoc("id", "17", field, "0.0"));
assertU(adoc("id", "18", field, "-0.0"));
assertU(adoc("id", "19", field, String.valueOf(Float.MIN_VALUE)));
assertU(adoc("id", "20", field, String.valueOf(Float.MAX_VALUE)));
assertU(adoc("id", "21", field, String.valueOf(Float.NEGATIVE_INFINITY)));
assertU(adoc("id", "22", field, String.valueOf(Float.POSITIVE_INFINITY)));
assertU(commit());
assertIntervalQuery(field, "[*,*]", "7");
assertIntervalQuery(field, "(*,*)", "7");
assertIntervalQuery(field, "(-1,1)", "3");
assertIntervalQuery(field, "(-2,1)", "4");
assertIntervalQuery(field, "(-1.3,0)", "1");
assertIntervalQuery(field, "[-1.3,0)", "2");
assertIntervalQuery(field, "[-1.3,0]", "3");
assertIntervalQuery(field, "(" + Float.NEGATIVE_INFINITY + ",0)", "2");
assertIntervalQuery(field, "(* ,0)", "3");
assertIntervalQuery(field, "[" + Float.NEGATIVE_INFINITY + ",0)", "3");
assertIntervalQuery(field, "(0, " + Float.MIN_VALUE + ")", "0");
assertIntervalQuery(field, "(0, " + Float.MIN_VALUE + "]", "1");
assertIntervalQuery(field, "(0, " + Float.MAX_VALUE + ")", "1");
assertIntervalQuery(field, "(0, " + Float.MAX_VALUE + "]", "2");
assertIntervalQuery(field, "(0, " + Float.POSITIVE_INFINITY + ")", "2");
assertIntervalQuery(field, "(0, " + Float.POSITIVE_INFINITY + "]", "3");
assertIntervalQuery(field, "[-0.0, 0.0]", "2");
assertIntervalQuery(field, "[-0.0, 0.0)", "1");
assertIntervalQuery(field, "(-0.0, 0.0]", "1");
if (testDouble) {
clearIndex();
assertU(adoc("id", "16", field, "-1.3"));
assertU(adoc("id", "17", field, "0.0"));
assertU(adoc("id", "18", field, "-0.0"));
assertU(adoc("id", "19", field, String.valueOf(Double.MIN_VALUE)));
assertU(adoc("id", "20", field, String.valueOf(Double.MAX_VALUE)));
assertU(adoc("id", "21", field, String.valueOf(Double.NEGATIVE_INFINITY)));
assertU(adoc("id", "22", field, String.valueOf(Double.POSITIVE_INFINITY)));
assertU(commit());
assertIntervalQuery(field, "[*,*]", "7");
assertIntervalQuery(field, "(*,*)", "7");
assertIntervalQuery(field, "(-1,1)", "3");
assertIntervalQuery(field, "(-2,1)", "4");
assertIntervalQuery(field, "(-1.3,0)", "1");
assertIntervalQuery(field, "[-1.3,0)", "2");
assertIntervalQuery(field, "[-1.3,0]", "3");
assertIntervalQuery(field, "(" + Double.NEGATIVE_INFINITY + ",0)", "2");
assertIntervalQuery(field, "(* ,0)", "3");
assertIntervalQuery(field, "[" + Double.NEGATIVE_INFINITY + ",0)", "3");
assertIntervalQuery(field, "(0, " + Double.MIN_VALUE + ")", "0");
assertIntervalQuery(field, "(0, " + Double.MIN_VALUE + "]", "1");
assertIntervalQuery(field, "(0, " + Double.MAX_VALUE + ")", "1");
assertIntervalQuery(field, "(0, " + Double.MAX_VALUE + "]", "2");
assertIntervalQuery(field, "(0, " + Double.POSITIVE_INFINITY + ")", "2");
assertIntervalQuery(field, "(0, " + Double.POSITIVE_INFINITY + "]", "3");
}
}
@Test
public void testDoubleFields() {
doTestFloat("test_d_dv", true);
}
@Test
public void testIntFields() {
assertU(adoc("id", "1", "test_i_dv", "0"));
assertU(adoc("id", "2", "test_i_dv", "1"));
assertU(adoc("id", "3", "test_i_dv", "2"));
assertU(adoc("id", "4", "test_i_dv", "3"));
assertU(adoc("id", "5", "test_i_dv", "4"));
assertU(adoc("id", "6", "test_i_dv", "5"));
assertU(adoc("id", "7", "test_i_dv", "6"));
assertU(adoc("id", "8", "test_i_dv", "7"));
assertU(adoc("id", "9", "test_i_dv", "8"));
assertU(adoc("id", "10"));
assertU(adoc("id", "11", "test_i_dv", "10"));
assertU(commit());
assertIntervalQueriesNumeric("test_i_dv");
}
@Test
public void testIntFieldsMultipleSegments() {
assertU(adoc("id", "1", "test_i_dv", "0"));
assertU(adoc("id", "2", "test_i_dv", "1"));
assertU(adoc("id", "3", "test_i_dv", "2"));
assertU(commit());
assertU(adoc("id", "4", "test_i_dv", "3"));
assertU(adoc("id", "5", "test_i_dv", "4"));
assertU(adoc("id", "6", "test_i_dv", "5"));
assertU(adoc("id", "7", "test_i_dv", "6"));
assertU(commit());
assertU(adoc("id", "8", "test_i_dv", "7"));
assertU(adoc("id", "9", "test_i_dv", "8"));
assertU(adoc("id", "10"));
assertU(adoc("id", "11", "test_i_dv", "10"));
assertU(commit());
int i = 12;
while (getNumberOfReaders() < 2 && i < 20) {
//try to get more than one segment
assertU(adoc("id", String.valueOf(i), "test_s_dv", String.valueOf(i)));
assertU(commit());
i++;
}
if (getNumberOfReaders() < 2) {
// It is OK if for some seeds we fall into this case (for example, TieredMergePolicy with
// segmentsPerTier=2). Most of the case we shouldn't and the test should proceed.
log.warn("Could not generate more than 1 segment for this seed. Will skip the test");
return;
}
assertIntervalQueriesNumeric("test_i_dv");
}
@Test
public void testIntMultivaluedFields() {
assertU(adoc("id", "1", "test_is_dv", "0"));
assertU(adoc("id", "2", "test_is_dv", "1"));
assertU(adoc("id", "3", "test_is_dv", "2"));
assertU(adoc("id", "4", "test_is_dv", "3"));
assertU(adoc("id", "5", "test_is_dv", "4"));
assertU(adoc("id", "6", "test_is_dv", "5"));
assertU(adoc("id", "7", "test_is_dv", "6"));
assertU(adoc("id", "8", "test_is_dv", "7"));
assertU(adoc("id", "9", "test_is_dv", "8"));
assertU(adoc("id", "10"));
assertU(adoc("id", "11", "test_is_dv", "10"));
assertU(commit());
assertIntervalQueriesNumeric("test_is_dv");
assertU(adoc("id", "1", "test_is_dv", "0", "test_is_dv", "1", "test_is_dv", "2", "test_is_dv", "3"));
assertU(adoc("id", "2", "test_is_dv", "1", "test_is_dv", "2", "test_is_dv", "3", "test_is_dv", "4"));
assertU(adoc("id", "3", "test_is_dv", "2", "test_is_dv", "3", "test_is_dv", "4", "test_is_dv", "5"));
assertU(adoc("id", "4", "test_is_dv", "3", "test_is_dv", "4", "test_is_dv", "5", "test_is_dv", "6"));
assertU(commit());
assertIntervalQuery("test_is_dv", "[1,3]", "4");
assertIntervalQuery("test_is_dv", "[3,3]", "4");
assertIntervalQuery("test_is_dv", "[5,9]", "6");
assertIntervalQuery("test_is_dv", "(5,9)", "4");
assertIntervalQuery("test_is_dv", "[*,*]", "10");
}
@Test
public void testDateFields() {
assertU(adoc("id", "1", "test_dt_dv", "2013-01-01T00:00:00Z"));
assertU(adoc("id", "2", "test_dt_dv", "2013-01-02T00:00:00Z"));
assertU(adoc("id", "3", "test_dt_dv", "2013-02-01T00:00:00Z"));
assertU(adoc("id", "4", "test_dt_dv", "2014-01-01T00:00:00Z"));
assertU(adoc("id", "5", "test_dt_dv", "2100-01-01T00:00:00Z"));
assertU(adoc("id", "6", "test_dt_dv", "2013-01-01T10:00:00Z"));
assertU(adoc("id", "7", "test_dt_dv", "2013-01-01T00:10:00Z"));
assertU(adoc("id", "8", "test_dt_dv", "2013-01-01T00:00:10Z"));
assertU(adoc("id", "9"));
assertU(commit());
assertIntervalQuery("test_dt_dv", "[*,*]", "8");
assertIntervalQuery("test_dt_dv", "[*,2014-01-01T00:00:00Z]", "7");
assertIntervalQuery("test_dt_dv", "[*,2014-01-01T00:00:00Z)", "6");
assertIntervalQuery("test_dt_dv", "[*,2014-01-01T00:00:00.001Z)", "7");
assertIntervalQuery("test_dt_dv", "[*,2013-12-31T23:59:59.999Z]", "6");
assertIntervalQuery("test_dt_dv", "[2013-12-31T23:59:59.9999Z,2014-01-01T00:00:00.001Z]", "1");
assertIntervalQuery("test_dt_dv", "[NOW,*]", "1");
assertIntervalQuery("test_dt_dv", "[*,NOW]", "7");
assertU(adoc("id", "5", "test_dt_dv", "NOW"));
assertU(commit());
assertIntervalQuery("test_dt_dv", "[NOW/DAY-1DAY,NOW+2DAY]", "1");
}
@Test
public void testWithDeletedDocs() {
assertU(adoc("id", "1", "test_s_dv", "dog"));
assertU(adoc("id", "2", "test_s_dv", "cat"));
assertU(adoc("id", "3", "test_s_dv", "bird"));
assertU(adoc("id", "16", "test_s_dv", "cat"));
assertU(adoc("id", "4", "test_s_dv", "turtle"));
assertU(adoc("id", "5", "test_s_dv", "\\goodbye,"));
assertU(adoc("id", "6", "test_s_dv", ",hello\\"));
assertU(adoc("id", "7", "test_s_dv", "dog"));
assertU(adoc("id", "15", "test_s_dv", "dog"));
assertU(adoc("id", "8", "test_s_dv", "dog"));
assertU(adoc("id", "9", "test_s_dv", "cat"));
assertU(adoc("id", "10"));
assertU(commit());
assertU(adoc("id", "11", "test_s_dv", "the"));
assertU(adoc("id", "12", "test_s_dv", "quick brown"));
assertU(adoc("id", "13", "test_s_dv", "fox"));
assertU(adoc("id", "14", "test_s_dv", "jumped over the lazy dog"));
assertU(commit());
assertU(delI("16"));
assertU(delI("15"));
assertU(delQ("id:[11 TO 14]"));
assertU(commit());
assertIntervalQueriesString("test_s_dv");
}
@Test
public void testChangeFieldKey() {
assertU(adoc("id", "1", "test_s_dv", "dog", "test_l_dv", "1"));
assertU(adoc("id", "2", "test_s_dv", "cat", "test_l_dv", "2"));
assertU(commit());
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "{!key=foo}test_s_dv",
"facet.interval", "{!key=bar}test_l_dv", "f.test_s_dv.facet.interval.set", "[cat,dog]",
"f.test_l_dv.facet.interval.set", "[0,1]",
"f.test_l_dv.facet.interval.set", "[2,*]"),
"//lst[@name='facet_intervals']/lst[@name='foo']/int[@name='[cat,dog]'][.=2]",
"//lst[@name='facet_intervals']/lst[@name='bar']/int[@name='[0,1]'][.=1]",
"//lst[@name='facet_intervals']/lst[@name='bar']/int[@name='[2,*]'][.=1]");
}
@Test
public void testFilterExclusion() {
assertU(adoc("id", "1", "test_s_dv", "dog"));
assertU(adoc("id", "2", "test_s_dv", "cat"));
assertU(adoc("id", "3", "test_s_dv", "bird"));
assertU(adoc("id", "4", "test_s_dv", "cat"));
assertU(adoc("id", "5", "test_s_dv", "turtle"));
assertU(adoc("id", "6", "test_s_dv", "dog"));
assertU(adoc("id", "7", "test_s_dv", "dog"));
assertU(adoc("id", "8", "test_s_dv", "dog"));
assertU(adoc("id", "9", "test_s_dv", "cat"));
assertU(adoc("id", "10"));
assertU(commit());
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv", "rows", "0",
"f.test_s_dv.facet.interval.set", "[a,d]",
"f.test_s_dv.facet.interval.set", "[d,z]"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[a,d]'][.=4]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[d,z]'][.=5]");
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv", "rows", "0",
"f.test_s_dv.facet.interval.set", "[a,d]",
"f.test_s_dv.facet.interval.set", "[d,z]",
"fq", "test_s_dv:dog"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[a,d]'][.=0]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[d,z]'][.=4]");
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "{!ex=dogs}test_s_dv", "rows", "0",
"f.test_s_dv.facet.interval.set", "[a,d]",
"f.test_s_dv.facet.interval.set", "[d,z]",
"fq", "{!tag='dogs'}test_s_dv:dog"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[a,d]'][.=4]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[d,z]'][.=5]");
}
@Test
public void testSolrJ() throws Exception {
assertU(adoc("id", "1", "test_i_dv", "0"));
assertU(adoc("id", "2", "test_i_dv", "1"));
assertU(adoc("id", "3", "test_i_dv", "2"));
assertU(commit());
// Don't close this client, it would shutdown the CoreContainer
@SuppressWarnings("resource")
SolrClient client = new EmbeddedSolrServer(h.getCoreContainer(), h.coreName);
SolrQuery q = new SolrQuery();
q.setQuery("*:*");
q.addIntervalFacets("test_i_dv", new String[]{"[0,1]","[2,*]"});
QueryResponse response = client.query(q);
assertEquals(1, response.getIntervalFacets().size());
assertEquals("test_i_dv", response.getIntervalFacets().get(0).getField());
assertEquals(2, response.getIntervalFacets().get(0).getIntervals().size());
assertEquals("[0,1]", response.getIntervalFacets().get(0).getIntervals().get(0).getKey());
assertEquals("[2,*]", response.getIntervalFacets().get(0).getIntervals().get(1).getKey());
q = new SolrQuery();
q.setQuery("*:*");
q.setFacet(true);
q.add("facet.interval", "{!key=foo}test_i_dv");
q.add("f.test_i_dv.facet.interval.set", "{!key=first}[0,1]");
q.add("f.test_i_dv.facet.interval.set", "{!key=second}[2,*]");
response = client.query(q);
assertEquals(1, response.getIntervalFacets().size());
assertEquals("foo", response.getIntervalFacets().get(0).getField());
assertEquals(2, response.getIntervalFacets().get(0).getIntervals().size());
assertEquals("first", response.getIntervalFacets().get(0).getIntervals().get(0).getKey());
assertEquals("second", response.getIntervalFacets().get(0).getIntervals().get(1).getKey());
}
private void assertIntervalQueriesNumeric(String field) {
assertIntervalQuery(field, "[0,1]", "2");
assertIntervalQuery(field, "(0,2)", "1");
assertIntervalQuery(field, "[0,2)", "2");
assertIntervalQuery(field, "(0,2]", "2");
assertIntervalQuery(field, "[*,5]", "6");
assertIntervalQuery(field, "[*,3)", "3", "[2,5)", "3", "[6,8)", "2", "[3,*]", "7", "[10,10]", "1", "[10,10]", "1", "[10,10]", "1");
assertIntervalQuery(field, "(5,*]", "4", "[5,5]", "1", "(*,5)", "5");
assertIntervalQuery(field, "[5,5]", "1", "(*,5)", "5", "(5,*]", "4");
assertIntervalQuery(field, "(5,*]", "4", "(*,5)", "5", "[5,5]", "1");
}
private void assertIntervalQueriesString(String field) {
assertIntervalQuery(field, "[bird,bird]", "1");
assertIntervalQuery(field, "(bird,dog)", "2");
assertIntervalQuery(field, "[bird,dog)", "3");
assertIntervalQuery(field, "(bird,turtle]", "6");
assertIntervalQuery(field, "[*,bird]", "3");
assertIntervalQuery(field, "[*,bird)", "2", "[bird,cat)", "1", "[cat,dog)", "2", "[dog,*]", "4");
assertIntervalQuery(field, "[*,*]", "9", "[*,dog)", "5", "[*,dog]", "8", "[dog,*]", "4");
assertIntervalQuery(field, field + ":dog", 3, "[*,*]", "3", "[*,dog)", "0", "[*,dog]", "3", "[dog,*]", "3", "[bird,cat]", "0");
assertIntervalQuery(field, "(*,dog)", "5", "[dog, dog]", "3", "(dog,*)", "1");
assertIntervalQuery(field, "[dog, dog]", "3", "(dog,*)", "1", "(*,dog)", "5");
assertIntervalQuery(field, "(dog,*)", "1", "(*,dog)", "5", "[dog, dog]", "3");
}
/**
* Will run a match all query, and ask for interval facets in the specified field.
* The intervals facet are indicated in the <code>intervals</code> parameter, followed
* by the expected count result. For example:
* <code>assertIntervalQuery("my_string_field", "[0,10]", "3", "(20,*), "12");</code>
*
* @param field The field in which the interval facet should be asked
* @param intervals a variable array of intervals followed by the expected count (also a string)
*/
private void assertIntervalQuery(String field, String... intervals) {
assertIntervalQuery(field, "*:*", -1, intervals);
}
private void assertIntervalQuery(String field, String query, int resultCount, String... intervals) {
assert (intervals.length & 1) == 0;
int idx = 0;
String[] params = new String[intervals.length + 6];
params[idx++] = "q";
params[idx++] = query;
params[idx++] = "facet";
params[idx++] = "true";
params[idx++] = "facet.interval";
params[idx++] = field;
for (int i = 0; i < intervals.length; i += 2) {
params[idx++] = "f." + field + ".facet.interval.set";
params[idx++] = intervals[i];
}
String[] tests = new String[intervals.length / 2 + (resultCount > 0 ? 1 : 0)];
idx = 0;
for (int i = 0; i < intervals.length; i += 2) {
tests[idx++] = "//lst[@name='facet_intervals']/lst[@name='" + field + "']/int[@name='" + intervals[i] + "'][.=" + intervals[i + 1] + "]";
}
if (resultCount >= 0) {
tests[idx++] = "//*[@numFound='" + resultCount + "']";
}
assertQ("Unexpected facet iterval count. Field:" + field + ", Intervals: " + Arrays.toString(intervals) + "Query: " + query,
req(params), tests);
}
}