/*
* 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 org.noggit.ObjectBuilder;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.util.TimeZoneUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
public class SimpleFacetsTest extends SolrTestCaseJ4 {
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solrconfig.xml","schema.xml");
createIndex();
}
static int random_commit_percent = 30;
static int random_dupe_percent = 25; // some duplicates in the index to create deleted docs
static void randomCommit(int percent_chance) {
if (random().nextInt(100) <= percent_chance)
assertU(commit());
}
static ArrayList<String[]> pendingDocs = new ArrayList<>();
// committing randomly gives different looking segments each time
static void add_doc(String... fieldsAndValues) {
do {
pendingDocs.add(fieldsAndValues);
} while (random().nextInt(100) <= random_dupe_percent);
// assertU(adoc(fieldsAndValues));
// randomCommit(random_commit_percent);
}
static void createIndex() throws Exception {
doEmptyFacetCounts(); // try on empty index
indexSimpleFacetCounts();
indexDateFacets();
indexFacetSingleValued();
indexFacetPrefixMultiValued();
indexFacetPrefixSingleValued();
indexSimpleGroupedFacetCounts();
Collections.shuffle(pendingDocs, random());
for (String[] doc : pendingDocs) {
assertU(adoc(doc));
randomCommit(random_commit_percent);
}
assertU(commit());
}
static void indexSimpleFacetCounts() {
add_doc("id", "42",
"range_facet_f", "35.3",
"trait_s", "Tool", "trait_s", "Obnoxious",
"name", "Zapp Brannigan",
"foo_s","A", "foo_s","B"
);
add_doc("id", "43" ,
"range_facet_f", "28.789",
"title", "Democratic Order of Planets",
"foo_s","A", "foo_s","B"
);
add_doc("id", "44",
"range_facet_f", "15.97",
"trait_s", "Tool",
"name", "The Zapper",
"foo_s","A", "foo_s","B", "foo_s","C"
);
add_doc("id", "45",
"range_facet_f", "30.0",
"trait_s", "Chauvinist",
"title", "25 star General",
"foo_s","A", "foo_s","B"
);
add_doc("id", "46",
"range_facet_f", "20.0",
"trait_s", "Obnoxious",
"subject", "Defeated the pacifists of the Gandhi nebula",
"foo_s","A", "foo_s","B"
);
add_doc("id", "47",
"range_facet_f", "28.62",
"trait_s", "Pig",
"text", "line up and fly directly at the enemy death cannons, clogging them with wreckage!",
"zerolen_s","",
"foo_s","A", "foo_s","B", "foo_s","C"
);
}
static void indexSimpleGroupedFacetCounts() {
add_doc("id", "2000", "hotel_s1", "a", "airport_s1", "ams", "duration_i1", "5");
add_doc("id", "2001", "hotel_s1", "a", "airport_s1", "dus", "duration_i1", "10");
add_doc("id", "2002", "hotel_s1", "b", "airport_s1", "ams", "duration_i1", "10");
add_doc("id", "2003", "hotel_s1", "b", "airport_s1", "ams", "duration_i1", "5");
add_doc("id", "2004", "hotel_s1", "b", "airport_s1", "ams", "duration_i1", "5");
}
@Test
public void testCachingBigTerms() throws Exception {
assertQ( req("indent","true", "q", "id:[42 TO 47]",
"facet", "true",
"facet.field", "foo_s" // big terms should cause foo_s:A to be cached
),
"*[count(//doc)=6]"
);
// now use the cached term as a filter to make sure deleted docs are accounted for
assertQ( req("indent","true", "fl","id", "q", "foo_s:B",
"facet", "true",
"facet.field", "foo_s",
"fq","foo_s:A"
),
"*[count(//doc)=6]"
);
}
@Test
public void testSimpleGroupedQueryRangeFacets() throws Exception {
// for the purposes of our test data, it shouldn't matter
// if we use facet.limit -100, -1, or 100 ...
// our set of values is small enough either way
testSimpleGroupedQueryRangeFacets("-100");
testSimpleGroupedQueryRangeFacets("-1");
testSimpleGroupedQueryRangeFacets("100");
}
private void testSimpleGroupedQueryRangeFacets(String facetLimit) {
assertQ(
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"group", "true",
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.limit", facetLimit,
"facet.query", "airport_s1:ams"
),
"//lst[@name='facet_queries']/int[@name='airport_s1:ams'][.='2']"
);
/* Testing facet.query using tagged filter query and exclusion */
assertQ(
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"fq", "{!tag=dus}airport_s1:dus",
"group", "true",
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.limit", facetLimit,
"facet.query", "{!ex=dus}airport_s1:ams"
),
"//lst[@name='facet_queries']/int[@name='{!ex=dus}airport_s1:ams'][.='2']"
);
assertQ(
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"group", "true",
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.limit", facetLimit,
"facet.range", "duration_i1",
"facet.range.start", "5",
"facet.range.end", "11",
"facet.range.gap", "1"
),
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='5'][.='2']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='6'][.='0']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='7'][.='0']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='8'][.='0']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='9'][.='0']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='10'][.='2']"
);
/* Testing facet.range using tagged filter query and exclusion */
assertQ(
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"fq", "{!tag=dus}airport_s1:dus",
"group", "true",
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.limit", facetLimit,
"facet.range", "{!ex=dus}duration_i1",
"facet.range.start", "5",
"facet.range.end", "11",
"facet.range.gap", "1"
),
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='5'][.='2']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='6'][.='0']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='7'][.='0']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='8'][.='0']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='9'][.='0']",
"//lst[@name='facet_ranges']/lst[@name='duration_i1']/lst[@name='counts']/int[@name='10'][.='2']"
);
}
@Test
public void testSimpleGroupedFacets() throws Exception {
// for the purposes of our test data, it shouldn't matter
// if we use facet.limit -100, -1, or 100 ...
// our set of values is small enough either way
testSimpleGroupedFacets("100");
testSimpleGroupedFacets("-100");
testSimpleGroupedFacets("-5");
testSimpleGroupedFacets("-1");
}
private void testSimpleGroupedFacets(String facetLimit) throws Exception {
assertQ(
"Return 5 docs with id range 1937 till 1940",
req("id:[2000 TO 2004]"),
"*[count(//doc)=5]"
);
assertQ(
"Return two facet counts for field airport_a",
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"group", "true",
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.limit", facetLimit,
"facet.field", "airport_s1"
),
"//lst[@name='facet_fields']/lst[@name='airport_s1']",
"*[count(//lst[@name='airport_s1']/int)=2]",
"//lst[@name='airport_s1']/int[@name='ams'][.='2']",
"//lst[@name='airport_s1']/int[@name='dus'][.='1']"
);
assertQ(
"Return one facet count for field airport_a using facet.offset",
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"group", "true",
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.offset", "1",
"facet.limit", facetLimit,
"facet.field", "airport_s1"
),
"//lst[@name='facet_fields']/lst[@name='airport_s1']",
"*[count(//lst[@name='airport_s1']/int)=1]",
"//lst[@name='airport_s1']/int[@name='dus'][.='1']"
);
assertQ(
"Return two facet counts for field airport_a with fq",
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"fq", "duration_i1:5",
"group", "true",
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.limit", facetLimit,
"facet.field", "airport_s1"
),
"//lst[@name='facet_fields']/lst[@name='airport_s1']",
"*[count(//lst[@name='airport_s1']/int)=2]",
"//lst[@name='airport_s1']/int[@name='ams'][.='2']",
"//lst[@name='airport_s1']/int[@name='dus'][.='0']"
);
assertQ(
"Return one facet count for field airport_s1 with prefix a",
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"group", "true",
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.field", "airport_s1",
"facet.limit", facetLimit,
"facet.prefix", "a"
),
"//lst[@name='facet_fields']/lst[@name='airport_s1']",
"*[count(//lst[@name='airport_s1']/int)=1]",
"//lst[@name='airport_s1']/int[@name='ams'][.='2']"
);
try {
h.query(
req(
"q", "*:*",
"fq", "id:[2000 TO 2004]",
"group.facet", "true",
"facet", "true",
"facet.field", "airport_s1",
"facet.prefix", "a"
)
);
fail("Exception should have been thrown");
} catch (SolrException e) {
assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
}
}
@Test
public void testEmptyFacetCounts() throws Exception {
doEmptyFacetCounts();
}
// static so we can try both with and without an empty index
static void doEmptyFacetCounts() throws Exception {
doEmptyFacetCounts("empty_t", new String[]{null, "myprefix",""});
doEmptyFacetCounts("empty_i", new String[]{null});
doEmptyFacetCounts("empty_f", new String[]{null});
doEmptyFacetCounts("empty_s", new String[]{null, "myprefix",""});
doEmptyFacetCounts("empty_d", new String[]{null});
}
static void doEmptyFacetCounts(String field, String[] prefixes) throws Exception {
SchemaField sf = h.getCore().getLatestSchema().getField(field);
String response = JQ(req("q", "*:*"));
Map rsp = (Map) ObjectBuilder.fromJSON(response);
Long numFound = (Long)(((Map)rsp.get("response")).get("numFound"));
ModifiableSolrParams params = params("q","*:*", "rows","0", "facet","true", "facet.field","{!key=myalias}"+field);
String[] methods = {null, "fc","enum","fcs"};
if (sf.multiValued() || sf.getType().multiValuedFieldCache()) {
methods = new String[]{null, "fc","enum"};
}
prefixes = prefixes==null ? new String[]{null} : prefixes;
for (String method : methods) {
if (method == null) {
params.remove("facet.method");
} else {
params.set("facet.method", method);
}
for (String prefix : prefixes) {
if (prefix == null) {
params.remove("facet.prefix");
} else {
params.set("facet.prefix", prefix);
}
for (String missing : new String[] {null, "true"}) {
if (missing == null) {
params.remove("facet.missing");
} else {
params.set("facet.missing", missing);
}
String expected = missing==null ? "[]" : "[null," + numFound + "]";
assertJQ(req(params),
"/facet_counts/facet_fields/myalias==" + expected);
}
}
}
}
@Test
public void testSimpleFacetCounts() {
assertQ("standard request handler returns all matches",
req("id:[42 TO 47]"),
"*[count(//doc)=6]"
);
assertQ("filter results using fq",
req("q","id:[42 TO 46]",
"fq", "id:[43 TO 47]"),
"*[count(//doc)=4]"
);
assertQ("don't filter results using blank fq",
req("q","id:[42 TO 46]",
"fq", " "),
"*[count(//doc)=5]"
);
assertQ("filter results using multiple fq params",
req("q","id:[42 TO 46]",
"fq", "trait_s:Obnoxious",
"fq", "id:[43 TO 47]"),
"*[count(//doc)=1]"
);
assertQ("check counts for facet queries",
req("q", "id:[42 TO 47]"
,"facet", "true"
,"facet.query", "trait_s:Obnoxious"
,"facet.query", "id:[42 TO 45]"
,"facet.query", "id:[43 TO 47]"
,"facet.field", "trait_s"
)
,"*[count(//doc)=6]"
,"//lst[@name='facet_counts']/lst[@name='facet_queries']"
,"//lst[@name='facet_queries']/int[@name='trait_s:Obnoxious'][.='2']"
,"//lst[@name='facet_queries']/int[@name='id:[42 TO 45]'][.='4']"
,"//lst[@name='facet_queries']/int[@name='id:[43 TO 47]'][.='5']"
,"//lst[@name='facet_counts']/lst[@name='facet_fields']"
,"//lst[@name='facet_fields']/lst[@name='trait_s']"
,"*[count(//lst[@name='trait_s']/int)=4]"
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='2']"
,"//lst[@name='trait_s']/int[@name='Pig'][.='1']"
);
assertQ("check multi-select facets with naming",
req("q", "id:[42 TO 47]"
,"facet", "true"
,"facet.query", "{!ex=1}trait_s:Obnoxious"
,"facet.query", "{!ex=2 key=foo}id:[42 TO 45]" // tag=2 same as 1
,"facet.query", "{!ex=3,4 key=bar}id:[43 TO 47]" // tag=3,4 don't exist
,"facet.field", "{!ex=3,1}trait_s" // 3,1 same as 1
,"fq", "{!tag=1,2}id:47" // tagged as 1 and 2
)
,"*[count(//doc)=1]"
,"//lst[@name='facet_counts']/lst[@name='facet_queries']"
,"//lst[@name='facet_queries']/int[@name='{!ex=1}trait_s:Obnoxious'][.='2']"
,"//lst[@name='facet_queries']/int[@name='foo'][.='4']"
,"//lst[@name='facet_queries']/int[@name='bar'][.='1']"
,"//lst[@name='facet_counts']/lst[@name='facet_fields']"
,"//lst[@name='facet_fields']/lst[@name='trait_s']"
,"*[count(//lst[@name='trait_s']/int)=4]"
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='2']"
,"//lst[@name='trait_s']/int[@name='Pig'][.='1']"
);
// test excluding main query
assertQ(req("q", "{!tag=main}id:43"
,"facet", "true"
,"facet.query", "{!key=foo}id:42"
,"facet.query", "{!ex=main key=bar}id:42" // only matches when we exclude main query
)
,"//lst[@name='facet_queries']/int[@name='foo'][.='0']"
,"//lst[@name='facet_queries']/int[@name='bar'][.='1']"
);
assertQ("check counts for applied facet queries using filtering (fq)",
req("q", "id:[42 TO 47]"
,"facet", "true"
,"fq", "id:[42 TO 45]"
,"facet.field", "trait_s"
,"facet.query", "id:[42 TO 45]"
,"facet.query", "id:[43 TO 47]"
)
,"*[count(//doc)=4]"
,"//lst[@name='facet_counts']/lst[@name='facet_queries']"
,"//lst[@name='facet_queries']/int[@name='id:[42 TO 45]'][.='4']"
,"//lst[@name='facet_queries']/int[@name='id:[43 TO 47]'][.='3']"
,"*[count(//lst[@name='trait_s']/int)=4]"
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='1']"
,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
,"//lst[@name='trait_s']/int[@name='Pig'][.='0']"
);
assertQ("check counts with facet.zero=false&facet.missing=true using fq",
req("q", "id:[42 TO 47]"
,"facet", "true"
,"facet.zeros", "false"
,"f.trait_s.facet.missing", "true"
,"fq", "id:[42 TO 45]"
,"facet.field", "trait_s"
)
,"*[count(//doc)=4]"
,"*[count(//lst[@name='trait_s']/int)=4]"
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='1']"
,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
,"//lst[@name='trait_s']/int[not(@name)][.='1']"
);
assertQ("check counts with facet.mincount=1&facet.missing=true using fq",
req("q", "id:[42 TO 47]"
,"facet", "true"
,"facet.mincount", "1"
,"f.trait_s.facet.missing", "true"
,"fq", "id:[42 TO 45]"
,"facet.field", "trait_s"
)
,"*[count(//doc)=4]"
,"*[count(//lst[@name='trait_s']/int)=4]"
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='1']"
,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
,"//lst[@name='trait_s']/int[not(@name)][.='1']"
);
assertQ("check counts with facet.mincount=2&facet.missing=true using fq",
req("q", "id:[42 TO 47]"
,"facet", "true"
,"facet.mincount", "2"
,"f.trait_s.facet.missing", "true"
,"fq", "id:[42 TO 45]"
,"facet.field", "trait_s"
)
,"*[count(//doc)=4]"
,"*[count(//lst[@name='trait_s']/int)=2]"
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
,"//lst[@name='trait_s']/int[not(@name)][.='1']"
);
assertQ("check sorted paging",
req("q", "id:[42 TO 47]"
,"facet", "true"
,"fq", "id:[42 TO 45]"
,"facet.field", "trait_s"
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","4"
)
,"*[count(//lst[@name='trait_s']/int)=4]"
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='1']"
,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
,"//lst[@name='trait_s']/int[@name='Pig'][.='0']"
);
// check that the default sort is by count
assertQ("check sorted paging",
req("q", "id:[42 TO 47]"
,"facet", "true"
,"fq", "id:[42 TO 45]"
,"facet.field", "trait_s"
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","3"
)
,"*[count(//lst[@name='trait_s']/int)=3]"
,"//int[1][@name='Tool'][.='2']"
,"//int[2][@name='Chauvinist'][.='1']"
,"//int[3][@name='Obnoxious'][.='1']"
);
//
// check that legacy facet.sort=true/false works
//
assertQ(req("q", "id:[42 TO 47]"
,"facet", "true"
,"fq", "id:[42 TO 45]"
,"facet.field", "trait_s"
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","3"
,"facet.sort","true" // true means sort-by-count
)
,"*[count(//lst[@name='trait_s']/int)=3]"
,"//int[1][@name='Tool'][.='2']"
,"//int[2][@name='Chauvinist'][.='1']"
,"//int[3][@name='Obnoxious'][.='1']"
);
assertQ(req("q", "id:[42 TO 47]"
,"facet", "true"
,"fq", "id:[42 TO 45]"
,"facet.field", "trait_s"
,"facet.mincount","1"
,"facet.offset","0"
,"facet.limit","3"
,"facet.sort","false" // false means sort by index order
)
,"*[count(//lst[@name='trait_s']/int)=3]"
,"//int[1][@name='Chauvinist'][.='1']"
,"//int[2][@name='Obnoxious'][.='1']"
,"//int[3][@name='Tool'][.='2']"
);
assertQ(req("q", "id:[42 TO 47]"
,"facet", "true"
,"facet.method","fc"
,"fq", "id:[42 TO 45]"
,"facet.field", "zerolen_s"
)
,"*[count(//lst[@name='zerolen_s']/int)=1]"
);
assertQ("a facet.query that analyzes to no query shoud not NPE",
req("q", "*:*",
"facet", "true",
"facet.query", "{!field key=k f=lengthfilt}a"),//2 char minimum
"//lst[@name='facet_queries']/int[@name='k'][.='0']"
);
}
public static void indexDateFacets() {
final String i = "id";
final String f = "bday";
final String ff = "a_tdt";
final String ooo = "00:00:00.000Z";
final String xxx = "15:15:15.155Z";
add_doc(i, "201", f, "1976-07-04T12:08:56.235Z", ff, "1900-01-01T"+ooo);
add_doc(i, "202", f, "1976-07-05T00:00:00.000Z", ff, "1976-07-01T"+ooo);
add_doc(i, "203", f, "1976-07-15T00:07:67.890Z", ff, "1976-07-04T"+ooo);
add_doc(i, "204", f, "1976-07-21T00:07:67.890Z", ff, "1976-07-05T"+ooo);
add_doc(i, "205", f, "1976-07-13T12:12:25.255Z", ff, "1976-07-05T"+xxx);
add_doc(i, "206", f, "1976-07-03T17:01:23.456Z", ff, "1976-07-07T"+ooo);
add_doc(i, "207", f, "1976-07-12T12:12:25.255Z", ff, "1976-07-13T"+ooo);
add_doc(i, "208", f, "1976-07-15T15:15:15.155Z", ff, "1976-07-13T"+xxx);
add_doc(i, "209", f, "1907-07-12T13:13:23.235Z", ff, "1976-07-15T"+xxx);
add_doc(i, "2010", f, "1976-07-03T11:02:45.678Z", ff, "2000-01-01T"+ooo);
add_doc(i, "2011", f, "1907-07-12T12:12:25.255Z");
add_doc(i, "2012", f, "2007-07-30T07:07:07.070Z");
add_doc(i, "2013", f, "1976-07-30T22:22:22.222Z");
add_doc(i, "2014", f, "1976-07-05T22:22:22.222Z");
}
@Test
public void testTrieDateFacets() {
helpTestDateFacets("bday", false);
}
@Test
public void testDateFacets() {
helpTestDateFacets("bday_pdt", false);
}
@Test
public void testTrieDateRangeFacets() {
helpTestDateFacets("bday", true);
}
@Test
public void testDateRangeFacets() {
helpTestDateFacets("bday_pdt", true);
}
private void helpTestDateFacets(final String fieldName,
final boolean rangeMode) {
final String p = rangeMode ? "facet.range" : "facet.date";
final String b = rangeMode ? "facet_ranges" : "facet_dates";
final String f = fieldName;
final String c = (rangeMode ? "/lst[@name='counts']" : "");
final String pre = "//lst[@name='"+b+"']/lst[@name='"+f+"']" + c;
final String meta = pre + (rangeMode ? "/../" : "");
// date faceting defaults to include both endpoints,
// range faceting defaults to including only lower
// doc exists with value @ 00:00:00.000 on July5
final String jul4 = rangeMode ? "[.='1' ]" : "[.='2' ]";
assertQ("check counts for month of facet by day",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-01T00:00:00.000Z+1MONTH"
,p+".gap", "+1DAY"
,p+".other", "all"
)
,"*[count("+pre+"/int)="+(rangeMode ? 31 : 34)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-04T00:00:00Z']" + jul4
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-13T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-14T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-15T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-16T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-17T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-18T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-19T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-21T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-22T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-23T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-24T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-25T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-26T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-27T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-28T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-29T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-30T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-31T00:00:00Z'][.='0']"
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='11']"
);
assertQ("check counts for month of facet by day with global mincount = 1",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-01T00:00:00.000Z+1MONTH"
,p+".gap", "+1DAY"
,p+".other", "all"
,"facet.mincount", "1"
)
,"*[count("+pre+"/int)="+(rangeMode ? 8 : 11)+"]"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-04T00:00:00Z']" + jul4
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-13T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-15T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-21T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-30T00:00:00Z'][.='1' ]"
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='11']"
);
assertQ("check counts for month of facet by day with field mincount = 1",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-01T00:00:00.000Z+1MONTH"
,p+".gap", "+1DAY"
,p+".other", "all"
,"f." + f + ".facet.mincount", "2"
)
,"*[count("+pre+"/int)="+(rangeMode ? 3 : 7)+"]"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='2' ]"
,pre+(rangeMode ? "" : "/int[@name='1976-07-04T00:00:00Z']" +jul4)
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-15T00:00:00Z'][.='2' ]"
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='11']"
);
assertQ("check before is not inclusive of upper bound by default",
req("q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-05T00:00:00.000Z"
,p+".end", "1976-07-07T00:00:00.000Z"
,p+".gap", "+1DAY"
,p+".other", "all"
)
,"*[count("+pre+"/int)="+(rangeMode ? 2 : 5)+"]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='0']"
,meta+"/int[@name='before' ][.='5']"
);
assertQ("check after is not inclusive of lower bound by default (for dates)",
req("q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-03T00:00:00.000Z"
,p+".end", "1976-07-05T00:00:00.000Z"
,p+".gap", "+1DAY"
,p+".other", "all"
)
,"*[count("+pre+"/int)="+(rangeMode ? 2 : 5)+"]"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-04T00:00:00Z']" + jul4
,meta+"/int[@name='after' ][.='"+(rangeMode ? 9 : 8)+"']"
);
assertQ("check hardend=false",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-13T00:00:00.000Z"
,p+".gap", "+5DAYS"
,p+".other", "all"
,p+".hardend","false"
)
,"*[count("+pre+"/int)="+(rangeMode ? 3 : 6)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='5' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='0' ]"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='4' ]"
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='3']"
,meta+"/int[@name='between'][.='9']"
);
assertQ("check hardend=true",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-13T00:00:00.000Z"
,p+".gap", "+5DAYS"
,p+".other", "all"
,p+".hardend","true"
)
,"*[count("+pre+"/int)="+(rangeMode ? 3 : 6)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='5' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='0' ]"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='1' ]"
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='6']"
,meta+"/int[@name='between'][.='6']"
);
}
@Test
public void testTrieDateFacetsWithIncludeOption() {
helpTestDateFacetsWithIncludeOption("a_tdt", false);
}
@Test
public void testDateFacetsWithIncludeOption() {
helpTestDateFacetsWithIncludeOption("a_pdt", false);
}
@Test
public void testTrieDateRangeFacetsWithIncludeOption() {
helpTestDateFacetsWithIncludeOption("a_tdt", true);
}
@Test
public void testDateRangeFacetsWithIncludeOption() {
helpTestDateFacetsWithIncludeOption("a_pdt", true);
}
/** similar to helpTestDateFacets, but for differnet fields with test data
exactly on on boundary marks */
private void helpTestDateFacetsWithIncludeOption(final String fieldName,
final boolean rangeMode) {
final String p = rangeMode ? "facet.range" : "facet.date";
final String b = rangeMode ? "facet_ranges" : "facet_dates";
final String f = fieldName;
final String c = (rangeMode ? "/lst[@name='counts']" : "");
final String pre = "//lst[@name='"+b+"']/lst[@name='"+f+"']" + c;
final String meta = pre + (rangeMode ? "/../" : "");
assertQ("checking counts for lower",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-16T00:00:00.000Z"
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "lower"
)
// 15 days + pre+post+inner = 18
,"*[count("+pre+"/int)="+(rangeMode ? 15 : 18)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-13T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-14T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-15T00:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='1']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='8']"
);
assertQ("checking counts for upper",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-16T00:00:00.000Z"
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "upper"
)
// 15 days + pre+post+inner = 18
,"*[count("+pre+"/int)="+(rangeMode ? 15 : 18)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-13T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-14T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-15T00:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='7']"
);
assertQ("checking counts for lower & upper",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-16T00:00:00.000Z"
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "lower"
,p+".include", "upper"
)
// 15 days + pre+post+inner = 18
,"*[count("+pre+"/int)="+(rangeMode ? 15 : 18)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-13T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-14T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-15T00:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='1']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='8']"
);
assertQ("checking counts for upper & edge",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-16T00:00:00.000Z"
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "upper"
,p+".include", "edge"
)
// 15 days + pre+post+inner = 18
,"*[count("+pre+"/int)="+(rangeMode ? 15 : 18)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-13T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-14T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-15T00:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='1']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='8']"
);
assertQ("checking counts for upper & outer",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-13T00:00:00.000Z" // smaller now
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "upper"
,p+".include", "outer"
)
// 12 days + pre+post+inner = 15
,"*[count("+pre+"/int)="+(rangeMode ? 12 : 15)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='4']"
,meta+"/int[@name='between'][.='5']"
);
assertQ("checking counts for lower & edge",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-13T00:00:00.000Z" // smaller now
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "lower"
,p+".include", "edge"
)
// 12 days + pre+post+inner = 15
,"*[count("+pre+"/int)="+(rangeMode ? 12 : 15)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='1']"
,meta+"/int[@name='after' ][.='3']"
,meta+"/int[@name='between'][.='6']"
);
assertQ("checking counts for lower & outer",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-13T00:00:00.000Z" // smaller now
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "lower"
,p+".include", "outer"
)
// 12 days + pre+post+inner = 15
,"*[count("+pre+"/int)="+(rangeMode ? 12 : 15)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='0']"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='4']"
,meta+"/int[@name='between'][.='5']"
);
assertQ("checking counts for lower & edge & outer",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-13T00:00:00.000Z" // smaller now
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "lower"
,p+".include", "edge"
,p+".include", "outer"
)
// 12 days + pre+post+inner = 15
,"*[count("+pre+"/int)="+(rangeMode ? 12 : 15)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='4']"
,meta+"/int[@name='between'][.='6']"
);
assertQ("checking counts for all",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,p, f
,p+".start", "1976-07-01T00:00:00.000Z"
,p+".end", "1976-07-13T00:00:00.000Z" // smaller now
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "all"
)
// 12 days + pre+post+inner = 15
,"*[count("+pre+"/int)="+(rangeMode ? 12 : 15)+"]"
,pre+"/int[@name='1976-07-01T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-02T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-04T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-05T00:00:00Z'][.='2' ]"
,pre+"/int[@name='1976-07-06T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-07T00:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-08T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T00:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T00:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='4']"
,meta+"/int[@name='between'][.='6']"
);
}
@Test
public void testDateFacetsWithTz() {
for (String field : new String[] { "a_tdt", "a_pdt"}) {
for (boolean rangeType : new boolean[] { true, false }) {
helpTestDateFacetsWithTz(field, rangeType);
}
}
}
private void helpTestDateFacetsWithTz(final String fieldName,
final boolean rangeMode) {
final String p = rangeMode ? "facet.range" : "facet.date";
final String b = rangeMode ? "facet_ranges" : "facet_dates";
final String f = fieldName;
final String c = (rangeMode ? "/lst[@name='counts']" : "");
final String pre = "//lst[@name='"+b+"']/lst[@name='"+f+"']" + c;
final String meta = pre + (rangeMode ? "/../" : "");
final String TZ = "America/Los_Angeles";
assumeTrue("Test requires JVM to know about about TZ: " + TZ,
TimeZoneUtils.KNOWN_TIMEZONE_IDS.contains(TZ));
assertQ("checking facet counts for fixed now, using TZ: " + TZ,
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"NOW", "205078333000" // 1976-07-01T14:12:13.000Z
,"TZ", TZ
,p, f
,p+".start", "NOW/MONTH"
,p+".end", "NOW/MONTH+15DAYS"
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "lower"
)
// 15 days + pre+post+inner = 18
,"*[count("+pre+"/int)="+(rangeMode ? 15 : 18)+"]"
,pre+"/int[@name='1976-07-01T07:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-02T07:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-03T07:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-04T07:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-05T07:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-06T07:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-07T07:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-08T07:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-09T07:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-10T07:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-11T07:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-12T07:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-13T07:00:00Z'][.='1' ]"
,pre+"/int[@name='1976-07-14T07:00:00Z'][.='0']"
,pre+"/int[@name='1976-07-15T07:00:00Z'][.='1' ]"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='7']"
);
// NOTE: the counts should all be zero, what we really care about
// is that the computed lower bounds take into account DST change
assertQ("checking facet counts arround DST change for TZ: " + TZ,
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"NOW", "1288606136000" // 2010-11-01T10:08:56.235Z
,"TZ", TZ
,p, f
,p+".start", "NOW/MONTH"
,p+".end", "NOW/MONTH+15DAYS"
,p+".gap", "+1DAY"
,p+".other", "all"
,p+".include", "lower"
)
// 15 days + pre+post+inner = 18
,"*[count("+pre+"/int)="+(rangeMode ? 15 : 18)+"]"
,pre+"/int[@name='2010-11-01T07:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-02T07:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-03T07:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-04T07:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-05T07:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-06T07:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-07T07:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-08T08:00:00Z'][.='0']" // BOOM!
,pre+"/int[@name='2010-11-09T08:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-10T08:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-11T08:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-12T08:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-13T08:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-14T08:00:00Z'][.='0']"
,pre+"/int[@name='2010-11-15T08:00:00Z'][.='0']"
);
}
@Test
public void testNumericRangeFacetsTrieFloat() {
helpTestFractionalNumberRangeFacets("range_facet_f");
}
@Test
public void testNumericRangeFacetsTrieDouble() {
helpTestFractionalNumberRangeFacets("range_facet_d");
}
@Test
public void testNumericRangeFacetsSortableFloat() {
helpTestFractionalNumberRangeFacets("range_facet_sf");
}
@Test
public void testNumericRangeFacetsSortableDouble() {
helpTestFractionalNumberRangeFacets("range_facet_sd");
}
@Test
public void testNumericRangeFacetsOverflowTrieDouble() {
helpTestNumericRangeFacetsDoubleOverflow("range_facet_d");
}
@Test
public void testNumericRangeFacetsOverflowSortableDouble() {
helpTestNumericRangeFacetsDoubleOverflow("range_facet_sd");
}
private void helpTestNumericRangeFacetsDoubleOverflow(final String fieldName) {
final String f = fieldName;
final String pre = "//lst[@name='facet_ranges']/lst[@name='"+f+"']/lst[@name='counts']";
final String meta = pre + "/../";
String start = "0.0";
String gap = (new Double( (double)Float.MAX_VALUE )).toString();
String end = (new Double( ((double)Float.MAX_VALUE) * 3D )).toString();
String mid = (new Double( ((double)Float.MAX_VALUE) * 2D )).toString();
assertQ(f+": checking counts for lower",
req( "q", "id:[30 TO 60]"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", start
,"facet.range.end", end
,"facet.range.gap", gap
,"facet.range.other", "all"
,"facet.range.include", "lower"
)
,"*[count("+pre+"/int)=3]"
,pre+"/int[@name='"+start+"'][.='6' ]"
,pre+"/int[@name='"+mid+"'][.='0' ]"
//
,meta+"/double[@name='end' ][.='"+end+"']"
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='6']"
);
}
private void helpTestFractionalNumberRangeFacets(final String fieldName) {
final String f = fieldName;
final String pre = "//lst[@name='facet_ranges']/lst[@name='"+f+"']/lst[@name='counts']";
final String meta = pre + "/../";
assertQ(f+": checking counts for lower",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "10"
,"facet.range.end", "50"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.include", "lower"
)
,"*[count("+pre+"/int)=4]"
,pre+"/int[@name='10.0'][.='1' ]"
,pre+"/int[@name='20.0'][.='3' ]"
,pre+"/int[@name='30.0'][.='2' ]"
,pre+"/int[@name='40.0'][.='0' ]"
//
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='6']"
);
assertQ(f + ":checking counts for upper",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "10"
,"facet.range.end", "50"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.include", "upper"
)
,"*[count("+pre+"/int)=4]"
,pre+"/int[@name='10.0'][.='2' ]"
,pre+"/int[@name='20.0'][.='3' ]"
,pre+"/int[@name='30.0'][.='1' ]"
,pre+"/int[@name='40.0'][.='0' ]"
//
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='6']"
);
assertQ(f + ":checking counts for lower & upper",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "10"
,"facet.range.end", "50"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.include", "upper"
,"facet.range.include", "lower"
)
,"*[count("+pre+"/int)=4]"
,pre+"/int[@name='10.0'][.='2' ]"
,pre+"/int[@name='20.0'][.='4' ]"
,pre+"/int[@name='30.0'][.='2' ]"
,pre+"/int[@name='40.0'][.='0' ]"
//
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='6']"
);
assertQ(f + ": checking counts for upper & edge",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "20"
,"facet.range.end", "50"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.include", "upper"
,"facet.range.include", "edge"
)
,"*[count("+pre+"/int)=3]"
,pre+"/int[@name='20.0'][.='4' ]"
,pre+"/int[@name='30.0'][.='1' ]"
,pre+"/int[@name='40.0'][.='0' ]"
//
,meta+"/int[@name='before' ][.='1']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='5']"
);
assertQ(f + ": checking counts for upper & outer",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "10"
,"facet.range.end", "30"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.include", "upper"
,"facet.range.include", "outer"
)
,"*[count("+pre+"/int)=2]"
,pre+"/int[@name='10.0'][.='2' ]"
,pre+"/int[@name='20.0'][.='3' ]"
//
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='2']"
,meta+"/int[@name='between'][.='5']"
);
assertQ(f + ": checking counts for lower & edge",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "10"
,"facet.range.end", "30"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.include", "lower"
,"facet.range.include", "edge"
)
,"*[count("+pre+"/int)=2]"
,pre+"/int[@name='10.0'][.='1' ]"
,pre+"/int[@name='20.0'][.='4' ]"
//
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='5']"
);
assertQ(f + ": checking counts for lower & outer",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "20"
,"facet.range.end", "40"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.include", "lower"
,"facet.range.include", "outer"
)
,"*[count("+pre+"/int)=2]"
,pre+"/int[@name='20.0'][.='3' ]"
,pre+"/int[@name='30.0'][.='2' ]"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='5']"
);
assertQ(f + ": checking counts for lower & edge & outer",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "20"
,"facet.range.end", "35.3"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.hardend", "true"
,"facet.range.include", "lower"
,"facet.range.include", "edge"
,"facet.range.include", "outer"
)
,"*[count("+pre+"/int)=2]"
,pre+"/int[@name='20.0'][.='3' ]"
,pre+"/int[@name='30.0'][.='2' ]"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='5']"
);
assertQ(f + ": checking counts for include all",
req( "q", "*:*"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "20"
,"facet.range.end", "35.3"
,"facet.range.gap", "10"
,"facet.range.other", "all"
,"facet.range.hardend", "true"
,"facet.range.include", "all"
)
,"*[count("+pre+"/int)=2]"
,pre+"/int[@name='20.0'][.='4' ]"
,pre+"/int[@name='30.0'][.='2' ]"
//
,meta+"/int[@name='before' ][.='2']"
,meta+"/int[@name='after' ][.='1']"
,meta+"/int[@name='between'][.='5']"
);
}
@Test
public void testNumericRangeFacetsTrieInt() {
helpTestWholeNumberRangeFacets("id");
}
@Test
public void testNumericRangeFacetsTrieLong() {
helpTestWholeNumberRangeFacets("range_facet_l");
}
@Test
public void testNumericRangeFacetsSortableInt() {
helpTestWholeNumberRangeFacets("range_facet_si");
}
@Test
public void testNumericRangeFacetsSortableLong() {
helpTestWholeNumberRangeFacets("range_facet_sl");
}
@Test
public void testNumericRangeFacetsOverflowTrieLong() {
helpTestNumericRangeFacetsLongOverflow("range_facet_l");
}
@Test
public void testNumericRangeFacetsOverflowSortableLong() {
helpTestNumericRangeFacetsLongOverflow("range_facet_sl");
}
private void helpTestNumericRangeFacetsLongOverflow(final String fieldName) {
final String f = fieldName;
final String pre = "//lst[@name='facet_ranges']/lst[@name='"+f+"']/lst[@name='counts']";
final String meta = pre + "/../";
String start = "0";
String gap = (new Long( (long)Integer.MAX_VALUE )).toString();
String end = (new Long( ((long)Integer.MAX_VALUE) * 3L )).toString();
String mid = (new Long( ((long)Integer.MAX_VALUE) * 2L )).toString();
assertQ(f+": checking counts for lower",
req( "q", "id:[30 TO 60]"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", start
,"facet.range.end", end
,"facet.range.gap", gap
,"facet.range.other", "all"
,"facet.range.include", "lower"
)
,"*[count("+pre+"/int)=3]"
,pre+"/int[@name='"+start+"'][.='6' ]"
,pre+"/int[@name='"+mid+"'][.='0' ]"
//
,meta+"/long[@name='end' ][.='"+end+"']"
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='6']"
);
}
private void helpTestWholeNumberRangeFacets(final String fieldName) {
// the float test covers a lot of the weird edge cases
// here we just need some basic sanity checking of the parsing
final String f = fieldName;
final String pre = "//lst[@name='facet_ranges']/lst[@name='"+f+"']/lst[@name='counts']";
final String meta = pre + "/../";
assertQ(f+": checking counts for lower",
req( "q", "id:[30 TO 60]"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "35"
,"facet.range.end", "50"
,"facet.range.gap", "5"
,"facet.range.other", "all"
,"facet.range.include", "lower"
)
,"*[count("+pre+"/int)=3]"
,pre+"/int[@name='35'][.='0' ]"
,pre+"/int[@name='40'][.='3' ]"
,pre+"/int[@name='45'][.='3' ]"
//
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='6']"
);
assertQ(f + ":checking counts for upper",
req( "q", "id:[30 TO 60]"
,"rows", "0"
,"facet", "true"
,"facet.range", f
,"facet.range.start", "35"
,"facet.range.end", "50"
,"facet.range.gap", "5"
,"facet.range.other", "all"
,"facet.range.include", "upper"
)
,"*[count("+pre+"/int)=3]"
,pre+"/int[@name='35'][.='0' ]"
,pre+"/int[@name='40'][.='4' ]"
,pre+"/int[@name='45'][.='2' ]"
//
,meta+"/int[@name='before' ][.='0']"
,meta+"/int[@name='after' ][.='0']"
,meta+"/int[@name='between'][.='6']"
);
}
static void indexFacetSingleValued() {
indexFacets("40","t_s1");
}
@Test
public void testFacetSingleValued() {
doFacets("t_s1");
}
@Test
public void testFacetSingleValuedFcs() {
doFacets("t_s1","facet.method","fcs");
}
static void indexFacets(String idPrefix, String f) {
add_doc("id", idPrefix+"1", f, "A");
add_doc("id", idPrefix+"2", f, "B");
add_doc("id", idPrefix+"3", f, "C");
add_doc("id", idPrefix+"4", f, "C");
add_doc("id", idPrefix+"5", f, "D");
add_doc("id", idPrefix+"6", f, "E");
add_doc("id", idPrefix+"7", f, "E");
add_doc("id", idPrefix+"8", f, "E");
add_doc("id", idPrefix+"9", f, "F");
add_doc("id", idPrefix+"10", f, "G");
add_doc("id", idPrefix+"11", f, "G");
add_doc("id", idPrefix+"12", f, "G");
add_doc("id", idPrefix+"13", f, "G");
add_doc("id", idPrefix+"14", f, "G");
}
public void doFacets(String f, String... params) {
String pre = "//lst[@name='"+f+"']";
String notc = "id:[* TO *] -"+f+":C";
assertQ("check counts for unlimited facet",
req(params, "q", "id:[* TO *]", "indent","true"
,"facet", "true"
,"facet.field", f
)
,"*[count(//lst[@name='facet_fields']/lst/int)=7]"
,pre+"/int[@name='G'][.='5']"
,pre+"/int[@name='E'][.='3']"
,pre+"/int[@name='C'][.='2']"
,pre+"/int[@name='A'][.='1']"
,pre+"/int[@name='B'][.='1']"
,pre+"/int[@name='D'][.='1']"
,pre+"/int[@name='F'][.='1']"
);
assertQ("check counts for facet with generous limit",
req(params, "q", "id:[* TO *]"
,"facet", "true"
,"facet.limit", "100"
,"facet.field", f
)
,"*[count(//lst[@name='facet_fields']/lst/int)=7]"
,pre+"/int[1][@name='G'][.='5']"
,pre+"/int[2][@name='E'][.='3']"
,pre+"/int[3][@name='C'][.='2']"
,pre+"/int[@name='A'][.='1']"
,pre+"/int[@name='B'][.='1']"
,pre+"/int[@name='D'][.='1']"
,pre+"/int[@name='F'][.='1']"
);
assertQ("check counts for limited facet",
req(params, "q", "id:[* TO *]"
,"facet", "true"
,"facet.limit", "2"
,"facet.field", f
)
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
,pre+"/int[1][@name='G'][.='5']"
,pre+"/int[2][@name='E'][.='3']"
);
assertQ("check offset",
req(params, "q", "id:[* TO *]"
,"facet", "true"
,"facet.offset", "1"
,"facet.limit", "1"
,"facet.field", f
)
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
,pre+"/int[1][@name='E'][.='3']"
);
assertQ("test sorted facet paging with zero (don't count in limit)",
req(params, "q", "id:[* TO *]"
,"fq",notc
,"facet", "true"
,"facet.field", f
,"facet.mincount","1"
,"facet.offset","0"
,"facet.limit","6"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=6]"
,pre+"/int[1][@name='G'][.='5']"
,pre+"/int[2][@name='E'][.='3']"
,pre+"/int[3][@name='A'][.='1']"
,pre+"/int[4][@name='B'][.='1']"
,pre+"/int[5][@name='D'][.='1']"
,pre+"/int[6][@name='F'][.='1']"
);
assertQ("test sorted facet paging with zero (test offset correctness)",
req(params, "q", "id:[* TO *]"
,"fq",notc
,"facet", "true"
,"facet.field", f
,"facet.mincount","1"
,"facet.offset","3"
,"facet.limit","2"
,"facet.sort","count"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
,pre+"/int[1][@name='B'][.='1']"
,pre+"/int[2][@name='D'][.='1']"
);
assertQ("test facet unsorted paging",
req(params, "q", "id:[* TO *]"
,"fq",notc
,"facet", "true"
,"facet.field", f
,"facet.mincount","1"
,"facet.offset","0"
,"facet.limit","6"
,"facet.sort","index"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=6]"
,pre+"/int[1][@name='A'][.='1']"
,pre+"/int[2][@name='B'][.='1']"
,pre+"/int[3][@name='D'][.='1']"
,pre+"/int[4][@name='E'][.='3']"
,pre+"/int[5][@name='F'][.='1']"
,pre+"/int[6][@name='G'][.='5']"
);
assertQ("test facet unsorted paging",
req(params, "q", "id:[* TO *]"
,"fq",notc
,"facet", "true"
,"facet.field", f
,"facet.mincount","1"
,"facet.offset","3"
,"facet.limit","2"
,"facet.sort","index"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
,pre+"/int[1][@name='E'][.='3']"
,pre+"/int[2][@name='F'][.='1']"
);
assertQ("test facet unsorted paging, mincount=2",
req(params, "q", "id:[* TO *]"
,"fq",notc
,"facet", "true"
,"facet.field", f
,"facet.mincount","2"
,"facet.offset","1"
,"facet.limit","2"
,"facet.sort","index"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
,pre+"/int[1][@name='G'][.='5']"
);
}
static void indexFacetPrefixMultiValued() {
indexFacetPrefix("50","t_s");
}
@Test
public void testFacetPrefixMultiValued() {
doFacetPrefix("t_s", null, "facet.method","enum");
doFacetPrefix("t_s", null, "facet.method", "enum", "facet.enum.cache.minDf", "3");
doFacetPrefix("t_s", null, "facet.method", "enum", "facet.enum.cache.minDf", "100");
doFacetPrefix("t_s", null, "facet.method", "fc");
}
static void indexFacetPrefixSingleValued() {
indexFacetPrefix("60","tt_s1");
}
@Test
public void testFacetPrefixSingleValued() {
doFacetPrefix("tt_s1", null);
}
@Test
public void testFacetPrefixSingleValuedFcs() {
doFacetPrefix("tt_s1", null, "facet.method","fcs");
doFacetPrefix("tt_s1", "{!threads=0}", "facet.method","fcs"); // direct execution
doFacetPrefix("tt_s1", "{!threads=-1}", "facet.method","fcs"); // default / unlimited threads
doFacetPrefix("tt_s1", "{!threads=2}", "facet.method","fcs"); // specific number of threads
}
static void indexFacetPrefix(String idPrefix, String f) {
add_doc("id", idPrefix+"1", f, "AAA");
add_doc("id", idPrefix+"2", f, "B");
add_doc("id", idPrefix+"3", f, "BB");
add_doc("id", idPrefix+"4", f, "BB");
add_doc("id", idPrefix+"5", f, "BBB");
add_doc("id", idPrefix+"6", f, "BBB");
add_doc("id", idPrefix+"7", f, "BBB");
add_doc("id", idPrefix+"8", f, "CC");
add_doc("id", idPrefix+"9", f, "CC");
add_doc("id", idPrefix+"10", f, "CCC");
add_doc("id", idPrefix+"11", f, "CCC");
add_doc("id", idPrefix+"12", f, "CCC");
assertU(commit());
}
public void doFacetPrefix(String f, String local, String... params) {
String indent="on";
String pre = "//lst[@name='"+f+"']";
String notc = "id:[* TO *] -"+f+":C";
String lf = local==null ? f : local+f;
assertQ("test facet.prefix middle, exact match first term",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","B"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=3]"
,pre+"/int[1][@name='BBB'][.='3']"
,pre+"/int[2][@name='BB'][.='2']"
,pre+"/int[3][@name='B'][.='1']"
);
assertQ("test facet.prefix middle, exact match first term, unsorted",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","index"
,"facet.prefix","B"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=3]"
,pre+"/int[1][@name='B'][.='1']"
,pre+"/int[2][@name='BB'][.='2']"
,pre+"/int[3][@name='BBB'][.='3']"
);
assertQ("test facet.prefix middle, exact match first term, unsorted",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","index"
,"facet.prefix","B"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=3]"
,pre+"/int[1][@name='B'][.='1']"
,pre+"/int[2][@name='BB'][.='2']"
,pre+"/int[3][@name='BBB'][.='3']"
);
assertQ("test facet.prefix middle, paging",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","1"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","B"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
,pre+"/int[1][@name='BB'][.='2']"
,pre+"/int[2][@name='B'][.='1']"
);
assertQ("test facet.prefix middle, paging",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","1"
,"facet.limit","1"
,"facet.sort","count"
,"facet.prefix","B"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
,pre+"/int[1][@name='BB'][.='2']"
);
assertQ("test facet.prefix middle, paging",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","1"
,"facet.limit","1"
,"facet.sort","count"
,"facet.prefix","B"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
,pre+"/int[1][@name='BB'][.='2']"
);
assertQ("test facet.prefix end, not exact match",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","C"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
,pre+"/int[1][@name='CCC'][.='3']"
,pre+"/int[2][@name='CC'][.='2']"
);
assertQ("test facet.prefix end, exact match",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","CC"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
,pre+"/int[1][@name='CCC'][.='3']"
,pre+"/int[2][@name='CC'][.='2']"
);
assertQ("test facet.prefix past end",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","X"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=0]"
);
assertQ("test facet.prefix past end",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","1"
,"facet.limit","-1"
,"facet.sort","count"
,"facet.prefix","X"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=0]"
);
assertQ("test facet.prefix at start, exact match",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","AAA"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
,pre+"/int[1][@name='AAA'][.='1']"
);
assertQ("test facet.prefix at Start, not exact match",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","AA"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
,pre+"/int[1][@name='AAA'][.='1']"
);
assertQ("test facet.prefix at Start, not exact match",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","AA"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
,pre+"/int[1][@name='AAA'][.='1']"
);
assertQ("test facet.prefix before start",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","0"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","999"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=0]"
);
assertQ("test facet.prefix before start",
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","0"
,"facet.offset","2"
,"facet.limit","100"
,"facet.sort","count"
,"facet.prefix","999"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=0]"
);
// test offset beyond what is collected internally in queue
assertQ(
req(params, "q", "id:[* TO *]"
,"indent",indent
,"facet","true"
,"facet.field", lf
,"facet.mincount","3"
,"facet.offset","5"
,"facet.limit","10"
,"facet.sort","count"
,"facet.prefix","CC"
)
,"*[count(//lst[@name='facet_fields']/lst/int)=0]"
);
}
/**
* kind of an absurd tests because if there is an inifnite loop, it
* would ver finish -- but at least it ensures that <i>if</i> one of
* these requests return, they return an error
*/
public void testRangeFacetInfiniteLoopDetection() {
for (String field : new String[] {"foo_f", "foo_sf",
"foo_d", "foo_sd",
"foo_i", "foo_si"}) {
assertQEx("no zero gap error: " + field,
req("q", "*:*",
"facet", "true",
"facet.range", field,
"facet.range.start", "23",
"facet.range.gap", "0",
"facet.range.end", "100"),
400);
}
for (String field : new String[] {"foo_pdt", "foo_dt"}) {
for (String type : new String[] {"date", "range"}) {
assertQEx("no zero gap error for facet." + type + ": " + field,
req("q", "*:*",
"facet", "true",
"facet." + type, field,
"facet."+type+".start", "NOW",
"facet."+type+".gap", "+0DAYS",
"facet."+type+".end", "NOW+10DAY"),
400);
}
}
for (String field : new String[] {"foo_f", "foo_sf"}) {
assertQEx("no float underflow error: " + field,
req("q", "*:*",
"facet", "true",
"facet.range", field,
"facet.range.start", "100000000000",
"facet.range.end", "100000086200",
"facet.range.gap", "2160"),
400);
}
for (String field : new String[] {"foo_d", "foo_sd"}) {
assertQEx("no double underflow error: " + field,
req("q", "*:*",
"facet", "true",
"facet.range", field,
"facet.range.start", "9900000000000",
"facet.range.end", "9900000086200",
"facet.range.gap", "0.0003"),
400);
}
}
}