SOLR-8835: fix faceting exception (uif) on multi-valued numeric docValues
[lucene-solr.git] / solr / core / src / test / org / apache / solr / search / facet / TestJsonFacets.java
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.solr.search.facet;
18
19 import java.nio.ByteBuffer;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Random;
28
29 import com.tdunning.math.stats.AVLTreeDigest;
30 import org.apache.solr.common.SolrException;
31 import org.apache.solr.util.hll.HLL;
32 import org.apache.lucene.util.LuceneTestCase;
33 import org.apache.lucene.util.packed.GrowableWriter;
34 import org.apache.lucene.util.packed.PackedInts;
35 import org.apache.solr.JSONTestUtil;
36 import org.apache.solr.SolrTestCaseHS;
37 import org.apache.solr.common.SolrInputDocument;
38 import org.apache.solr.common.params.ModifiableSolrParams;
39 import org.apache.solr.request.macro.MacroExpander;
40 import org.junit.AfterClass;
41 import org.junit.BeforeClass;
42 import org.junit.Test;
43
44 @LuceneTestCase.SuppressCodecs({"Lucene3x","Lucene40","Lucene41","Lucene42","Lucene45","Appending"})
45 public class TestJsonFacets extends SolrTestCaseHS {
46
47 private static SolrInstances servers; // for distributed testing
48 private static int origTableSize;
49
50 @BeforeClass
51 public static void beforeTests() throws Exception {
52 JSONTestUtil.failRepeatedKeys = true;
53 origTableSize = FacetFieldProcessorNumeric.MAXIMUM_STARTING_TABLE_SIZE;
54 FacetFieldProcessorNumeric.MAXIMUM_STARTING_TABLE_SIZE=2; // stress test resizing
55 initCore("solrconfig-tlog.xml","schema_latest.xml");
56 }
57
58 public static void initServers() throws Exception {
59 if (servers == null) {
60 servers = new SolrInstances(3, "solrconfig-tlog.xml", "schema_latest.xml");
61 }
62 }
63
64 @AfterClass
65 public static void afterTests() throws Exception {
66 JSONTestUtil.failRepeatedKeys = false;
67 FacetFieldProcessorNumeric.MAXIMUM_STARTING_TABLE_SIZE=origTableSize;
68 if (servers != null) {
69 servers.stop();
70 servers = null;
71 }
72 }
73
74 // attempt to reproduce https://github.com/Heliosearch/heliosearch/issues/33
75 @Test
76 public void testComplex() throws Exception {
77 Random r = random();
78
79 Client client = Client.localClient;
80
81 double price_low = 11000;
82 double price_high = 100000;
83
84 ModifiableSolrParams p = params("make_s","make_s", "model_s","model_s", "price_low",Double.toString(price_low), "price_high",Double.toString(price_high));
85
86
87 MacroExpander m = new MacroExpander( p.getMap() );
88
89 String make_s = m.expand("${make_s}");
90 String model_s = m.expand("${model_s}");
91
92 client.deleteByQuery("*:*", null);
93
94
95 int nDocs = 99;
96 String[] makes = {"honda", "toyota", "ford", null};
97 Double[] prices = {10000.0, 30000.0, 50000.0, 0.0, null};
98 String[] honda_models = {"accord", "civic", "fit", "pilot", null}; // make sure this is alphabetized to match tiebreaks in index
99 String[] other_models = {"z1", "z2", "z3", "z4", "z5", "z6", null};
100
101 int nHonda = 0;
102 final int[] honda_model_counts = new int[honda_models.length];
103
104 for (int i=0; i<nDocs; i++) {
105 SolrInputDocument doc = sdoc("id", Integer.toString(i));
106
107 Double price = rand(prices);
108 if (price != null) {
109 doc.addField("cost_f", price);
110 }
111 boolean matches_price = price!=null && price >= price_low && price <= price_high;
112
113 String make = rand(makes);
114 if (make != null) {
115 doc.addField(make_s, make);
116 }
117
118 if ("honda".equals(make)) {
119 int modelNum = r.nextInt(honda_models.length);
120 String model = honda_models[modelNum];
121 if (model != null) {
122 doc.addField(model_s, model);
123 }
124 if (matches_price) {
125 nHonda++;
126 honda_model_counts[modelNum]++;
127 }
128 } else if (make == null) {
129 doc.addField(model_s, rand(honda_models)); // add some docs w/ model but w/o make
130 } else {
131 // other makes
132 doc.addField(model_s, rand(other_models)); // add some docs w/ model but w/o make
133 }
134
135 client.add(doc, null);
136 if (r.nextInt(10) == 0) {
137 client.add(doc, null); // dup, causing a delete
138 }
139 if (r.nextInt(20) == 0) {
140 client.commit(); // force new seg
141 }
142 }
143
144 client.commit();
145
146 // now figure out top counts
147 List<Integer> idx = new ArrayList<>();
148 for (int i=0; i<honda_model_counts.length-1; i++) {
149 idx.add(i);
150 }
151 Collections.sort(idx, new Comparator<Integer>() {
152 @Override
153 public int compare(Integer o1, Integer o2) {
154 int cmp = honda_model_counts[o2] - honda_model_counts[o1];
155 return cmp == 0 ? o1 - o2 : cmp;
156 }
157 });
158
159
160
161 // straight query facets
162 client.testJQ(params(p, "q", "*:*", "rows","0", "fq","+${make_s}:honda +cost_f:[${price_low} TO ${price_high}]"
163 , "json.facet", "{makes:{terms:{field:${make_s}, facet:{models:{terms:{field:${model_s}, limit:2, mincount:0}}}}}}}"
164 , "facet","true", "facet.pivot","make_s,model_s", "facet.limit", "2"
165 )
166 , "facets=={count:" + nHonda + ", makes:{buckets:[{val:honda, count:" + nHonda + ", models:{buckets:["
167 + "{val:" + honda_models[idx.get(0)] + ", count:" + honda_model_counts[idx.get(0)] + "},"
168 + "{val:" + honda_models[idx.get(1)] + ", count:" + honda_model_counts[idx.get(1)] + "}]}"
169 + "}]}}"
170 );
171
172
173 }
174
175
176 public void indexSimple(Client client) throws Exception {
177 client.deleteByQuery("*:*", null);
178 client.add(sdoc("id", "1", "cat_s", "A", "where_s", "NY", "num_d", "4", "num_i", "2", "val_b", "true", "sparse_s", "one"), null);
179 client.add(sdoc("id", "2", "cat_s", "B", "where_s", "NJ", "num_d", "-9", "num_i", "-5", "val_b", "false"), null);
180 client.add(sdoc("id", "3"), null);
181 client.commit();
182 client.add(sdoc("id", "4", "cat_s", "A", "where_s", "NJ", "num_d", "2", "num_i", "3"), null);
183 client.add(sdoc("id", "5", "cat_s", "B", "where_s", "NJ", "num_d", "11", "num_i", "7", "sparse_s", "two"),null);
184 client.commit();
185 client.add(sdoc("id", "6", "cat_s", "B", "where_s", "NY", "num_d", "-5", "num_i", "-5"),null);
186 client.commit();
187 }
188
189
190 public void testStatsSimple() throws Exception {
191 Client client = Client.localClient();
192 indexSimple(client);
193
194 // test multiple json.facet commands
195 assertJQ(req("q", "*:*", "rows", "0"
196 , "json.facet", "{x:'sum(num_d)'}"
197 , "json.facet", "{y:'min(num_d)'}"
198 )
199 , "facets=={count:6 , x:3.0, y:-9.0 }"
200 );
201
202
203 // test streaming
204 assertJQ(req("q", "*:*", "rows", "0"
205 , "json.facet", "{ cat:{terms:{field:'cat_s', method:stream }}" +
206 ", cat2:{terms:{field:'cat_s', method:stream, sort:'index asc' }}" + // default sort
207 ", cat3:{terms:{field:'cat_s', method:stream, mincount:3 }}" + // mincount
208 ", cat4:{terms:{field:'cat_s', method:stream, prefix:B }}" + // prefix
209 ", cat5:{terms:{field:'cat_s', method:stream, offset:1 }}" + // offset
210 " }"
211 )
212 , "facets=={count:6 " +
213 ", cat :{buckets:[{val:A, count:2},{val:B, count:3}]}" +
214 ", cat2:{buckets:[{val:A, count:2},{val:B, count:3}]}" +
215 ", cat3:{buckets:[{val:B, count:3}]}" +
216 ", cat4:{buckets:[{val:B, count:3}]}" +
217 ", cat5:{buckets:[{val:B, count:3}]}" +
218 " }"
219 );
220
221
222 // test nested streaming under non-streaming
223 assertJQ(req("q", "*:*", "rows", "0"
224 , "json.facet", "{ cat:{terms:{field:'cat_s', sort:'index asc', facet:{where:{terms:{field:where_s,method:stream}}} }}}"
225 )
226 , "facets=={count:6 " +
227 ", cat :{buckets:[{val:A, count:2, where:{buckets:[{val:NJ,count:1},{val:NY,count:1}]} },{val:B, count:3, where:{buckets:[{val:NJ,count:2},{val:NY,count:1}]} }]}"
228 + "}"
229 );
230
231 // test nested streaming under streaming
232 assertJQ(req("q", "*:*", "rows", "0"
233 , "json.facet", "{ cat:{terms:{field:'cat_s', method:stream, facet:{where:{terms:{field:where_s,method:stream}}} }}}"
234 )
235 , "facets=={count:6 " +
236 ", cat :{buckets:[{val:A, count:2, where:{buckets:[{val:NJ,count:1},{val:NY,count:1}]} },{val:B, count:3, where:{buckets:[{val:NJ,count:2},{val:NY,count:1}]} }]}"
237 + "}"
238 );
239
240 // test nested streaming with stats under streaming
241 assertJQ(req("q", "*:*", "rows", "0"
242 , "json.facet", "{ cat:{terms:{field:'cat_s', method:stream, facet:{ where:{terms:{field:where_s,method:stream, facet:{x:'max(num_d)'} }}} }}}"
243 )
244 , "facets=={count:6 " +
245 ", cat :{buckets:[{val:A, count:2, where:{buckets:[{val:NJ,count:1,x:2.0},{val:NY,count:1,x:4.0}]} },{val:B, count:3, where:{buckets:[{val:NJ,count:2,x:11.0},{val:NY,count:1,x:-5.0}]} }]}"
246 + "}"
247 );
248
249 // test nested streaming with stats under streaming with stats
250 assertJQ(req("q", "*:*", "rows", "0",
251 "facet","true"
252 , "json.facet", "{ cat:{terms:{field:'cat_s', method:stream, facet:{ y:'min(num_d)', where:{terms:{field:where_s,method:stream, facet:{x:'max(num_d)'} }}} }}}"
253 )
254 , "facets=={count:6 " +
255 ", cat :{buckets:[{val:A, count:2, y:2.0, where:{buckets:[{val:NJ,count:1,x:2.0},{val:NY,count:1,x:4.0}]} },{val:B, count:3, y:-9.0, where:{buckets:[{val:NJ,count:2,x:11.0},{val:NY,count:1,x:-5.0}]} }]}"
256 + "}"
257 );
258
259
260 assertJQ(req("q", "*:*", "fq","cat_s:A")
261 , "response/numFound==2"
262 );
263 }
264
265 Map<String,String[]> suffixMap = new HashMap<>();
266 {
267 suffixMap.put("_s", new String[]{"_s","_ss","_sd","_sds"} );
268 suffixMap.put("_ss", new String[]{"_ss","_sds"} );
269 suffixMap.put("_l", new String[]{"_l","_ls","_ld","_lds"} );
270 suffixMap.put("_ls", new String[]{"_ls","_lds"} );
271 suffixMap.put("_i", new String[]{"_i","_is","_id","_ids", "_l","_ls","_ld","_lds"} );
272 suffixMap.put("_is", new String[]{"_is","_ids", "_ls","_lds"} );
273 suffixMap.put("_d", new String[]{"_d","_ds","_dd","_dds"} );
274 suffixMap.put("_ds", new String[]{"_ds","_dds"} );
275 suffixMap.put("_f", new String[]{"_f","_fs","_fd","_fds", "_d","_ds","_dd","_dds"} );
276 suffixMap.put("_fs", new String[]{"_fs","_fds","_ds","_dds"} );
277 suffixMap.put("_dt", new String[]{"_dt","_dts","_dtd","_dtds"} );
278 suffixMap.put("_dts", new String[]{"_dts","_dtds"} );
279 suffixMap.put("_b", new String[]{"_b"} );
280 }
281
282 List<String> getAlternatives(String field) {
283 int idx = field.lastIndexOf("_");
284 if (idx<=0 || idx>=field.length()) return Collections.singletonList(field);
285 String suffix = field.substring(idx);
286 String[] alternativeSuffixes = suffixMap.get(suffix);
287 if (alternativeSuffixes == null) return Collections.singletonList(field);
288 String base = field.substring(0, idx);
289 List<String> out = new ArrayList<>(alternativeSuffixes.length);
290 for (String altS : alternativeSuffixes) {
291 out.add( base + altS );
292 }
293 Collections.shuffle(out, random());
294 return out;
295 }
296
297 @Test
298 public void testStats() throws Exception {
299 doStats(Client.localClient, params("debugQuery", Boolean.toString(random().nextBoolean()) ));
300 }
301
302 @Test
303 public void testDistrib() throws Exception {
304 initServers();
305 Client client = servers.getClient(random().nextInt());
306 client.queryDefaults().set( "shards", servers.getShards(), "debugQuery", Boolean.toString(random().nextBoolean()) );
307 doStats( client, params() );
308 }
309
310 public void doStats(Client client, ModifiableSolrParams p) throws Exception {
311
312 Map<String, List<String>> fieldLists = new HashMap<>();
313 fieldLists.put("noexist", getAlternatives("noexist_s"));
314 fieldLists.put("cat_s", getAlternatives("cat_s"));
315 fieldLists.put("where_s", getAlternatives("where_s"));
316 fieldLists.put("num_d", getAlternatives("num_f")); // num_d name is historical, which is why we map it to num_f alternatives so we can include floats as well
317 fieldLists.put("num_i", getAlternatives("num_i"));
318 fieldLists.put("super_s", getAlternatives("super_s"));
319 fieldLists.put("val_b", getAlternatives("val_b"));
320 fieldLists.put("date", getAlternatives("date_dt"));
321 fieldLists.put("sparse_s", getAlternatives("sparse_s"));
322 fieldLists.put("multi_ss", getAlternatives("multi_ss"));
323
324 // TODO: if a field will be used as a function source, we can't use multi-valued types for it (currently)
325
326 int maxAlt = 0;
327 for (List<String> fieldList : fieldLists.values()) {
328 maxAlt = Math.max(fieldList.size(), maxAlt);
329 }
330
331 // take the field with the maximum number of alternative types and loop through our variants that many times
332 for (int i=0; i<maxAlt; i++) {
333 ModifiableSolrParams args = params(p);
334 for (String field : fieldLists.keySet()) {
335 List<String> alts = fieldLists.get(field);
336 String alt = alts.get( i % alts.size() );
337 args.add(field, alt);
338 }
339
340 args.set("rows","0");
341 // doStatsTemplated(client, args);
342 }
343
344
345 // single valued strings
346 doStatsTemplated(client, params(p, "rows","0", "noexist","noexist_s", "cat_s","cat_s", "where_s","where_s", "num_d","num_d", "num_i","num_i", "super_s","super_s", "val_b","val_b", "date","date_dt", "sparse_s","sparse_s" ,"multi_ss","multi_ss") );
347
348 // multi-valued strings, long/float substitute for int/double
349 doStatsTemplated(client, params(p, "facet","true", "rows","0", "noexist","noexist_ss", "cat_s","cat_ss", "where_s","where_ss", "num_d","num_f", "num_i","num_l", "num_is","num_ls", "num_fs", "num_ds", "super_s","super_ss", "val_b","val_b", "date","date_dt", "sparse_s","sparse_ss", "multi_ss","multi_ss") );
350
351 // multi-valued strings, method=dv for terms facets
352 doStatsTemplated(client, params(p, "terms", "method:dv,", "rows", "0", "noexist", "noexist_ss", "cat_s", "cat_ss", "where_s", "where_ss", "num_d", "num_f", "num_i", "num_l", "super_s", "super_ss", "val_b", "val_b", "date", "date_dt", "sparse_s", "sparse_ss", "multi_ss", "multi_ss"));
353
354 // single valued docvalues for strings, and single valued numeric doc values for numeric fields
355 doStatsTemplated(client, params(p, "rows","0", "noexist","noexist_sd", "cat_s","cat_sd", "where_s","where_sd", "num_d","num_dd", "num_i","num_id", "num_is","num_lds", "num_fs","num_dds", "super_s","super_sd", "val_b","val_b", "date","date_dtd", "sparse_s","sparse_sd" ,"multi_ss","multi_sds") );
356
357 // multi-valued docvalues
358 FacetFieldProcessorDV.unwrap_singleValued_multiDv = false; // better multi-valued coverage
359 doStatsTemplated(client, params(p, "rows","0", "noexist","noexist_sds", "cat_s","cat_sds", "where_s","where_sds", "num_d","num_d", "num_i","num_i", "num_is","num_ids", "num_fs","num_fds", "super_s","super_sds", "val_b","val_b", "date","date_dtds", "sparse_s","sparse_sds" ,"multi_ss","multi_sds") );
360
361 // multi-valued docvalues
362 FacetFieldProcessorDV.unwrap_singleValued_multiDv = true;
363 doStatsTemplated(client, params(p, "rows","0", "noexist","noexist_sds", "cat_s","cat_sds", "where_s","where_sds", "num_d","num_d", "num_i","num_i", "num_is","num_ids", "num_fs","num_fds", "super_s","super_sds", "val_b","val_b", "date","date_dtds", "sparse_s","sparse_sds" ,"multi_ss","multi_sds") );
364 }
365
366 public static void doStatsTemplated(Client client, ModifiableSolrParams p) throws Exception {
367 p.set("Z_num_i", "Z_" + p.get("num_i") );
368 if (p.get("num_is") == null) p.add("num_is","num_is");
369 if (p.get("num_fs") == null) p.add("num_fs","num_fs");
370
371 MacroExpander m = new MacroExpander( p.getMap() );
372
373 String cat_s = m.expand("${cat_s}");
374 String where_s = m.expand("${where_s}");
375 String num_d = m.expand("${num_d}");
376 String num_i = m.expand("${num_i}");
377 String num_is = m.expand("${num_is}");
378 String num_fs = m.expand("${num_fs}");
379 String Z_num_i = m.expand("${Z_num_i}");
380 String val_b = m.expand("${val_b}");
381 String date = m.expand("${date}");
382 String super_s = m.expand("${super_s}");
383 String sparse_s = m.expand("${sparse_s}");
384 String multi_ss = m.expand("${multi_ss}");
385
386 client.deleteByQuery("*:*", null);
387
388 SolrInputDocument doc =
389 sdoc("id", "1", cat_s, "A", where_s, "NY", num_d, "4", num_i, "2", num_is,"2",num_is,"-5", num_fs,"2",num_fs,"-5", super_s, "zodiac", date, "2001-01-01T01:01:01Z", val_b, "true", sparse_s, "one");
390 client.add(doc, null);
391 client.add(doc, null);
392 client.add(doc, null); // a couple of deleted docs
393 client.add(sdoc("id", "2", cat_s, "B", where_s, "NJ", num_d, "-9", num_i, "-5", num_is,"3",num_is,"-1", num_fs,"3",num_fs,"-1.5", super_s,"superman", date,"2002-02-02T02:02:02Z", val_b, "false" , multi_ss,"a", multi_ss,"b" , Z_num_i, "0"), null);
394 client.add(sdoc("id", "3"), null);
395 client.commit();
396 client.add(sdoc("id", "4", cat_s, "A", where_s, "NJ", num_d, "2", num_i, "3", num_is,"0",num_is,"3", num_fs,"0", num_fs,"3", super_s,"spiderman", date,"2003-03-03T03:03:03Z" , multi_ss, "b", Z_num_i, ""+Integer.MIN_VALUE), null);
397 client.add(sdoc("id", "5", cat_s, "B", where_s, "NJ", num_d, "11", num_i, "7", num_is,"0", num_fs,"0", super_s,"batman" , date,"2001-02-03T01:02:03Z" ,sparse_s,"two", multi_ss, "a"), null);
398 client.commit();
399 client.add(sdoc("id", "6", cat_s, "B", where_s, "NY", num_d, "-5", num_i, "-5", num_is,"-1", num_fs,"-1.5", super_s,"hulk" , date,"2002-03-01T03:02:01Z" , multi_ss, "b", multi_ss, "a", Z_num_i, ""+Integer.MAX_VALUE), null);
400 client.commit();
401
402 // test for presence of debugging info
403 ModifiableSolrParams debugP = params(p);
404 debugP.set("debugQuery","true");
405 client.testJQ(params(debugP, "q", "*:*"
406 , "json.facet", "{catA:{query:{q:'${cat_s}:A'}}, catA2:{query:{query:'${cat_s}:A'}}, catA3:{query:'${cat_s}:A'} }"
407 )
408 , "facets=={ 'count':6, 'catA':{ 'count':2}, 'catA2':{ 'count':2}, 'catA3':{ 'count':2}}"
409 , "debug/facet-trace==" // just test for presence, not exact structure / values
410 );
411
412
413 // straight query facets
414 client.testJQ(params(p, "q", "*:*"
415 , "json.facet", "{catA:{query:{q:'${cat_s}:A'}}, catA2:{query:{query:'${cat_s}:A'}}, catA3:{query:'${cat_s}:A'} }"
416 )
417 , "facets=={ 'count':6, 'catA':{ 'count':2}, 'catA2':{ 'count':2}, 'catA3':{ 'count':2}}"
418 );
419
420 // nested query facets
421 client.testJQ(params(p, "q", "*:*"
422 , "json.facet", "{ catB:{type:query, q:'${cat_s}:B', facet:{nj:{query:'${where_s}:NJ'}, ny:{query:'${where_s}:NY'}} }}"
423 )
424 , "facets=={ 'count':6, 'catB':{'count':3, 'nj':{'count':2}, 'ny':{'count':1}}}"
425 );
426
427 // nested query facets on subset
428 client.testJQ(params(p, "q", "id:(2 3)"
429 , "json.facet", "{ catB:{query:{q:'${cat_s}:B', facet:{nj:{query:'${where_s}:NJ'}, ny:{query:'${where_s}:NY'}} }}}"
430 )
431 , "facets=={ 'count':2, 'catB':{'count':1, 'nj':{'count':1}, 'ny':{'count':0}}}"
432 );
433
434 // nested query facets with stats
435 client.testJQ(params(p, "q", "*:*"
436 , "json.facet", "{ catB:{query:{q:'${cat_s}:B', facet:{nj:{query:{q:'${where_s}:NJ'}}, ny:{query:'${where_s}:NY'}} }}}"
437 )
438 , "facets=={ 'count':6, 'catB':{'count':3, 'nj':{'count':2}, 'ny':{'count':1}}}"
439 );
440
441
442 // field/terms facet
443 client.testJQ(params(p, "q", "*:*"
444 , "json.facet", "{c1:{field:'${cat_s}'}, c2:{field:{field:'${cat_s}'}}, c3:{${terms} type:terms, field:'${cat_s}'} }"
445 )
446 , "facets=={ 'count':6, " +
447 "'c1':{ 'buckets':[{ 'val':'B', 'count':3}, { 'val':'A', 'count':2}]}, " +
448 "'c2':{ 'buckets':[{ 'val':'B', 'count':3}, { 'val':'A', 'count':2}]}, " +
449 "'c3':{ 'buckets':[{ 'val':'B', 'count':3}, { 'val':'A', 'count':2}]}} "
450 );
451
452 // test mincount
453 client.testJQ(params(p, "q", "*:*"
454 , "json.facet", "{f1:{terms:{${terms} field:'${cat_s}', mincount:3}}}"
455 )
456 , "facets=={ 'count':6, " +
457 "'f1':{ 'buckets':[{ 'val':'B', 'count':3}]} } "
458 );
459
460 // test default mincount of 1
461 client.testJQ(params(p, "q", "id:1"
462 , "json.facet", "{f1:{terms:'${cat_s}'}}"
463 )
464 , "facets=={ 'count':1, " +
465 "'f1':{ 'buckets':[{ 'val':'A', 'count':1}]} } "
466 );
467
468 // test mincount of 0 - need processEmpty for distrib to match up
469 client.testJQ(params(p, "q", "id:1"
470 , "json.facet", "{processEmpty:true, f1:{terms:{${terms} field:'${cat_s}', mincount:0}}}"
471 )
472 , "facets=={ 'count':1, " +
473 "'f1':{ 'buckets':[{ 'val':'A', 'count':1}, { 'val':'B', 'count':0}]} } "
474 );
475
476 // test mincount of 0 with stats, need processEmpty for distrib to match up
477 client.testJQ(params(p, "q", "id:1"
478 , "json.facet", "{processEmpty:true, f1:{terms:{${terms} field:'${cat_s}', mincount:0, allBuckets:true, facet:{n1:'sum(${num_d})'} }}}"
479 )
480 , "facets=={ 'count':1, " +
481 "'f1':{ allBuckets:{ 'count':1, n1:4.0}, 'buckets':[{ 'val':'A', 'count':1, n1:4.0}, { 'val':'B', 'count':0 /*, n1:0.0 */ }]} } "
482 );
483
484 // test sorting by other stats
485 client.testJQ(params(p, "q", "*:*"
486 , "json.facet", "{f1:{terms:{${terms} field:'${cat_s}', sort:'n1 desc', facet:{n1:'sum(${num_d})'} }}" +
487 " , f2:{terms:{${terms} field:'${cat_s}', sort:'n1 asc', facet:{n1:'sum(${num_d})'} }} }"
488 )
489 , "facets=={ 'count':6, " +
490 " f1:{ 'buckets':[{ val:'A', count:2, n1:6.0 }, { val:'B', count:3, n1:-3.0}]}" +
491 ", f2:{ 'buckets':[{ val:'B', count:3, n1:-3.0}, { val:'A', count:2, n1:6.0 }]} }"
492 );
493
494 // test sorting by other stats
495 client.testJQ(params(p, "q", "*:*"
496 , "json.facet", "{f1:{${terms} type:terms, field:'${cat_s}', sort:'x desc', facet:{x:'min(${num_d})'} }" +
497 " , f2:{${terms} type:terms, field:'${cat_s}', sort:'x desc', facet:{x:'max(${num_d})'} } " +
498 " , f3:{${terms} type:terms, field:'${cat_s}', sort:'x desc', facet:{x:'unique(${where_s})'} } " +
499 " , f4:{${terms} type:terms, field:'${cat_s}', sort:'x desc', facet:{x:'hll(${where_s})'} } " +
500 "}"
501 )
502 , "facets=={ 'count':6, " +
503 " f1:{ 'buckets':[{ val:'A', count:2, x:2.0 }, { val:'B', count:3, x:-9.0}]}" +
504 ", f2:{ 'buckets':[{ val:'B', count:3, x:11.0 }, { val:'A', count:2, x:4.0 }]} " +
505 ", f3:{ 'buckets':[{ val:'A', count:2, x:2 }, { val:'B', count:3, x:2 }]} " +
506 ", f4:{ 'buckets':[{ val:'A', count:2, x:2 }, { val:'B', count:3, x:2 }]} " +
507 "}"
508 );
509
510 // test sorting by stat with function
511 client.testJQ(params(p, "q", "*:*"
512 , "json.facet", "{f1:{terms:{${terms} field:'${cat_s}', sort:'n1 desc', facet:{n1:'avg(add(${num_d},${num_d}))'} }}" +
513 " , f2:{terms:{${terms} field:'${cat_s}', sort:'n1 asc', facet:{n1:'avg(add(${num_d},${num_d}))'} }} }"
514 )
515 , "facets=={ 'count':6, " +
516 " f1:{ 'buckets':[{ val:'A', count:2, n1:6.0 }, { val:'B', count:3, n1:-2.0}]}" +
517 ", f2:{ 'buckets':[{ val:'B', count:3, n1:-2.0}, { val:'A', count:2, n1:6.0 }]} }"
518 );
519
520
521
522 // percentiles 0,10,50,90,100
523 // catA: 2.0 2.2 3.0 3.8 4.0
524 // catB: -9.0 -8.2 -5.0 7.800000000000001 11.0
525 // all: -9.0 -7.3999999999999995 2.0 8.200000000000001 11.0
526 // test sorting by single percentile
527 client.testJQ(params(p, "q", "*:*"
528 , "json.facet", "{f1:{terms:{${terms} field:'${cat_s}', sort:'n1 desc', facet:{n1:'percentile(${num_d},50)'} }}" +
529 " , f2:{terms:{${terms} field:'${cat_s}', sort:'n1 asc', facet:{n1:'percentile(${num_d},50)'} }} }"
530 )
531 , "facets=={ 'count':6, " +
532 " f1:{ 'buckets':[{ val:'A', count:2, n1:3.0 }, { val:'B', count:3, n1:-5.0}]}" +
533 ", f2:{ 'buckets':[{ val:'B', count:3, n1:-5.0}, { val:'A', count:2, n1:3.0 }]} }"
534 );
535
536 // test sorting by multiple percentiles (sort is by first)
537 client.testJQ(params(p, "q", "*:*"
538 , "json.facet", "{f1:{terms:{${terms} field:${cat_s}, sort:'n1 desc', facet:{n1:'percentile(${num_d},50,0,100)'} }}" +
539 " , f2:{terms:{${terms} field:${cat_s}, sort:'n1 asc', facet:{n1:'percentile(${num_d},50,0,100)'} }} }"
540 )
541 , "facets=={ 'count':6, " +
542 " f1:{ 'buckets':[{ val:'A', count:2, n1:[3.0,2.0,4.0] }, { val:'B', count:3, n1:[-5.0,-9.0,11.0] }]}" +
543 ", f2:{ 'buckets':[{ val:'B', count:3, n1:[-5.0,-9.0,11.0]}, { val:'A', count:2, n1:[3.0,2.0,4.0] }]} }"
544 );
545
546 // test sorting by count/index order
547 client.testJQ(params(p, "q", "*:*"
548 , "json.facet", "{f1:{terms:{${terms} field:'${cat_s}', sort:'count desc' } }" +
549 " , f2:{terms:{${terms} field:'${cat_s}', sort:'count asc' } }" +
550 " , f3:{terms:{${terms} field:'${cat_s}', sort:'index asc' } }" +
551 " , f4:{terms:{${terms} field:'${cat_s}', sort:'index desc' } }" +
552 "}"
553 )
554 , "facets=={ count:6 " +
555 " ,f1:{buckets:[ {val:B,count:3}, {val:A,count:2} ] }" +
556 " ,f2:{buckets:[ {val:A,count:2}, {val:B,count:3} ] }" +
557 " ,f3:{buckets:[ {val:A,count:2}, {val:B,count:3} ] }" +
558 " ,f4:{buckets:[ {val:B,count:3}, {val:A,count:2} ] }" +
559 "}"
560 );
561
562 // test sorting by default count/index order
563 client.testJQ(params(p, "q", "*:*"
564 , "json.facet", "{f1:{terms:{${terms} field:'${cat_s}', sort:'count' } }" +
565 " , f2:{terms:{${terms} field:'${cat_s}', sort:'count asc' } }" +
566 " , f3:{terms:{${terms} field:'${cat_s}', sort:'index' } }" +
567 " , f4:{terms:{${terms} field:'${cat_s}', sort:'index desc' } }" +
568 "}"
569 )
570 , "facets=={ count:6 " +
571 " ,f1:{buckets:[ {val:B,count:3}, {val:A,count:2} ] }" +
572 " ,f2:{buckets:[ {val:A,count:2}, {val:B,count:3} ] }" +
573 " ,f3:{buckets:[ {val:A,count:2}, {val:B,count:3} ] }" +
574 " ,f4:{buckets:[ {val:B,count:3}, {val:A,count:2} ] }" +
575 "}"
576 );
577
578
579 // test tiebreaks when sorting by count
580 client.testJQ(params(p, "q", "id:1 id:6"
581 , "json.facet", "{f1:{terms:{${terms} field:'${cat_s}', sort:'count desc' } }" +
582 " , f2:{terms:{${terms} field:'${cat_s}', sort:'count asc' } }" +
583 "}"
584 )
585 , "facets=={ count:2 " +
586 " ,f1:{buckets:[ {val:A,count:1}, {val:B,count:1} ] }" +
587 " ,f2:{buckets:[ {val:A,count:1}, {val:B,count:1} ] }" +
588 "}"
589 );
590
591 // terms facet with nested query facet
592 client.testJQ(params(p, "q", "*:*"
593 , "json.facet", "{cat:{terms:{${terms} field:'${cat_s}', facet:{nj:{query:'${where_s}:NJ'}} } }} }"
594 )
595 , "facets=={ 'count':6, " +
596 "'cat':{ 'buckets':[{ 'val':'B', 'count':3, 'nj':{ 'count':2}}, { 'val':'A', 'count':2, 'nj':{ 'count':1}}]} }"
597 );
598
599 // terms facet with nested query facet on subset
600 client.testJQ(params(p, "q", "id:(2 5 4)"
601 , "json.facet", "{cat:{terms:{${terms} field:'${cat_s}', facet:{nj:{query:'${where_s}:NJ'}} } }} }"
602 )
603 , "facets=={ 'count':3, " +
604 "'cat':{ 'buckets':[{ 'val':'B', 'count':2, 'nj':{ 'count':2}}, { 'val':'A', 'count':1, 'nj':{ 'count':1}}]} }"
605 );
606
607 // test prefix
608 client.testJQ(params(p, "q", "*:*"
609 , "json.facet", "{f1:{terms:{${terms} field:${super_s}, prefix:s, mincount:0 }}}" // even with mincount=0, we should only see buckets with the prefix
610 )
611 , "facets=={ 'count':6, " +
612 "'f1':{ 'buckets':[{val:spiderman, count:1}, {val:superman, count:1}]} } "
613 );
614
615 // test prefix that doesn't exist
616 client.testJQ(params(p, "q", "*:*"
617 , "json.facet", "{f1:{terms:{${terms} field:${super_s}, prefix:ttt, mincount:0 }}}"
618 )
619 , "facets=={ 'count':6, " +
620 "'f1':{ 'buckets':[]} } "
621 );
622
623 // test prefix that doesn't exist at start
624 client.testJQ(params(p, "q", "*:*"
625 , "json.facet", "{f1:{terms:{${terms} field:${super_s}, prefix:aaaaaa, mincount:0 }}}"
626 )
627 , "facets=={ 'count':6, " +
628 "'f1':{ 'buckets':[]} } "
629 );
630
631 // test prefix that doesn't exist at end
632 client.testJQ(params(p, "q", "*:*"
633 , "json.facet", "{f1:{terms:{${terms} field:${super_s}, prefix:zzzzzz, mincount:0 }}}"
634 )
635 , "facets=={ 'count':6, " +
636 "'f1':{ 'buckets':[]} } "
637 );
638
639 // test prefix on where field
640 client.testJQ(params(p, "q", "*:*"
641 , "json.facet", "{" +
642 " f1:{${terms} type:terms, field:${where_s}, prefix:N }" +
643 ",f2:{${terms} type:terms, field:${where_s}, prefix:NY }" +
644 ",f3:{${terms} type:terms, field:${where_s}, prefix:NJ }" +
645 "}"
646 )
647 , "facets=={ 'count':6 " +
648 ",f1:{ 'buckets':[ {val:NJ,count:3}, {val:NY,count:2} ]}" +
649 ",f2:{ 'buckets':[ {val:NY,count:2} ]}" +
650 ",f3:{ 'buckets':[ {val:NJ,count:3} ]}" +
651 " } "
652 );
653
654 // test prefix on real multi-valued field
655 client.testJQ(params(p, "q", "*:*"
656 , "json.facet", "{" +
657 " f1:{${terms} type:terms, field:${multi_ss}, prefix:A }" +
658 ",f2:{${terms} type:terms, field:${multi_ss}, prefix:z }" +
659 ",f3:{${terms} type:terms, field:${multi_ss}, prefix:aa }" +
660 ",f4:{${terms} type:terms, field:${multi_ss}, prefix:bb }" +
661 ",f5:{${terms} type:terms, field:${multi_ss}, prefix:a }" +
662 ",f6:{${terms} type:terms, field:${multi_ss}, prefix:b }" +
663 "}"
664 )
665 , "facets=={ 'count':6 " +
666 ",f1:{buckets:[]}" +
667 ",f2:{buckets:[]}" +
668 ",f3:{buckets:[]}" +
669 ",f4:{buckets:[]}" +
670 ",f5:{buckets:[ {val:a,count:3} ]}" +
671 ",f6:{buckets:[ {val:b,count:3} ]}" +
672 " } "
673 );
674
675 //
676 // missing
677 //
678
679 // test missing w/ non-existent field
680 client.testJQ(params(p, "q", "*:*"
681 , "json.facet", "{f1:{terms:{${terms} field:${noexist}, missing:true}}}"
682 )
683 , "facets=={ 'count':6, " +
684 "'f1':{ 'buckets':[], missing:{count:6} } } "
685 );
686
687 // test missing
688 client.testJQ(params(p, "q", "*:*"
689 , "json.facet", "{f1:{terms:{${terms} field:${sparse_s}, missing:true }}}"
690 )
691 , "facets=={ 'count':6, " +
692 "'f1':{ 'buckets':[{val:one, count:1}, {val:two, count:1}], missing:{count:4} } } "
693 );
694
695 // test missing with stats
696 client.testJQ(params(p, "q", "*:*"
697 , "json.facet", "{f1:{terms:{${terms} field:${sparse_s}, missing:true, facet:{x:'sum(${num_d})'} }}}"
698 )
699 , "facets=={ 'count':6, " +
700 "'f1':{ 'buckets':[{val:one, count:1, x:4.0}, {val:two, count:1, x:11.0}], missing:{count:4, x:-12.0} } } "
701 );
702
703 // test that the missing bucket is not affected by any prefix
704 client.testJQ(params(p, "q", "*:*"
705 , "json.facet", "{f1:{terms:{${terms} field:${sparse_s}, missing:true, prefix:on, facet:{x:'sum(${num_d})'} }}}"
706 )
707 , "facets=={ 'count':6, " +
708 "'f1':{ 'buckets':[{val:one, count:1, x:4.0}], missing:{count:4, x:-12.0} } } "
709 );
710
711 // test missing with prefix that doesn't exist
712 client.testJQ(params(p, "q", "*:*"
713 , "json.facet", "{f1:{terms:{${terms} field:${sparse_s}, missing:true, prefix:ppp, facet:{x:'sum(${num_d})'} }}}"
714 )
715 , "facets=={ 'count':6, " +
716 "'f1':{ 'buckets':[], missing:{count:4, x:-12.0} } } "
717 );
718
719 // test numBuckets
720 client.testJQ(params(p, "q", "*:*", "rows", "0", "facet", "true"
721 , "json.facet", "{f1:{terms:{${terms} field:${cat_s}, numBuckets:true, limit:1}}}" // TODO: limit:0 produced an error
722 )
723 , "facets=={ 'count':6, " +
724 "'f1':{ numBuckets:2, buckets:[{val:B, count:3}]} } "
725 );
726
727 // prefix should lower numBuckets
728 client.testJQ(params(p, "q", "*:*", "rows", "0", "facet", "true"
729 , "json.facet", "{f1:{terms:{${terms} field:${cat_s}, numBuckets:true, prefix:B}}}"
730 )
731 , "facets=={ 'count':6, " +
732 "'f1':{ numBuckets:1, buckets:[{val:B, count:3}]} } "
733 );
734
735 // mincount should lower numBuckets
736 client.testJQ(params(p, "q", "*:*", "rows", "0", "facet", "true"
737 , "json.facet", "{f1:{terms:{${terms} field:${cat_s}, numBuckets:true, mincount:3}}}"
738 )
739 , "facets=={ 'count':6, " +
740 "'f1':{ numBuckets:1, buckets:[{val:B, count:3}]} } "
741 );
742
743 // basic range facet
744 client.testJQ(params(p, "q", "*:*"
745 , "json.facet", "{f:{type:range, field:${num_d}, start:-5, end:10, gap:5}}"
746 )
747 , "facets=={count:6, f:{buckets:[ {val:-5.0,count:1}, {val:0.0,count:2}, {val:5.0,count:0} ] } }"
748 );
749
750 // basic range facet on dates
751 client.testJQ(params(p, "q", "*:*"
752 , "json.facet", "{f:{type:range, field:${date}, start:'2001-01-01T00:00:00Z', end:'2003-01-01T00:00:00Z', gap:'+1YEAR'}}"
753 )
754 , "facets=={count:6, f:{buckets:[ {val:'2001-01-01T00:00:00Z',count:2}, {val:'2002-01-01T00:00:00Z',count:2}] } }"
755 );
756
757 // range facet on dates w/ stats
758 client.testJQ(params(p, "q", "*:*"
759 , "json.facet", "{f:{type:range, field:${date}, start:'2002-01-01T00:00:00Z', end:'2005-01-01T00:00:00Z', gap:'+1YEAR', other:all, facet:{ x:'avg(${num_d})' } } }"
760 )
761 , "facets=={count:6, f:{buckets:[ {val:'2002-01-01T00:00:00Z',count:2,x:-7.0}, {val:'2003-01-01T00:00:00Z',count:1,x:2.0}, {val:'2004-01-01T00:00:00Z',count:0}], before:{count:2,x:7.5}, after:{count:0}, between:{count:3,x:-4.0} } }"
762 );
763
764 // basic range facet with "include" params
765 client.testJQ(params(p, "q", "*:*"
766 , "json.facet", "{f:{range:{field:${num_d}, start:-5, end:10, gap:5, include:upper}}}"
767 )
768 , "facets=={count:6, f:{buckets:[ {val:-5.0,count:0}, {val:0.0,count:2}, {val:5.0,count:0} ] } }"
769 );
770
771 // range facet with sub facets and stats
772 client.testJQ(params(p, "q", "*:*"
773 , "json.facet", "{f:{range:{field:${num_d}, start:-5, end:10, gap:5, facet:{ x:'sum(${num_i})', ny:{query:'${where_s}:NY'}} }}}"
774 )
775 , "facets=={count:6, f:{buckets:[ {val:-5.0,count:1,x:-5.0,ny:{count:1}}, {val:0.0,count:2,x:5.0,ny:{count:1}}, {val:5.0,count:0 /* ,x:0.0,ny:{count:0} */ } ] } }"
776 );
777
778 // range facet with sub facets and stats, with "other:all"
779 client.testJQ(params(p, "q", "*:*"
780 , "json.facet", "{f:{range:{field:${num_d}, start:-5, end:10, gap:5, other:all, facet:{ x:'sum(${num_i})', ny:{query:'${where_s}:NY'}} }}}"
781 )
782 , "facets=={count:6, f:{buckets:[ {val:-5.0,count:1,x:-5.0,ny:{count:1}}, {val:0.0,count:2,x:5.0,ny:{count:1}}, {val:5.0,count:0 /* ,x:0.0,ny:{count:0} */} ]" +
783 ",before: {count:1,x:-5.0,ny:{count:0}}" +
784 ",after: {count:1,x:7.0, ny:{count:0}}" +
785 ",between:{count:3,x:0.0, ny:{count:2}}" +
786 " } }"
787 );
788
789 // range facet with mincount
790 client.testJQ(params(p, "q", "*:*"
791 , "json.facet", "{f:{type:range, field:${num_d}, start:-5, end:10, gap:5, other:all, mincount:2, facet:{ x:'sum(${num_i})', ny:{query:'${where_s}:NY'}} }}"
792 )
793 , "facets=={count:6, f:{buckets:[ {val:0.0,count:2,x:5.0,ny:{count:1}} ]" +
794 ",before: {count:1,x:-5.0,ny:{count:0}}" +
795 ",after: {count:1,x:7.0, ny:{count:0}}" +
796 ",between:{count:3,x:0.0, ny:{count:2}}" +
797 " } }"
798 );
799
800 // range facet with sub facets and stats, with "other:all", on subset
801 client.testJQ(params(p, "q", "id:(3 4 6)"
802 , "json.facet", "{f:{range:{field:${num_d}, start:-5, end:10, gap:5, other:all, facet:{ x:'sum(${num_i})', ny:{query:'${where_s}:NY'}} }}}"
803 )
804 , "facets=={count:3, f:{buckets:[ {val:-5.0,count:1,x:-5.0,ny:{count:1}}, {val:0.0,count:1,x:3.0,ny:{count:0}}, {val:5.0,count:0 /* ,x:0.0,ny:{count:0} */} ]" +
805 ",before: {count:0 /* ,x:0.0,ny:{count:0} */ }" +
806 ",after: {count:0 /* ,x:0.0,ny:{count:0} */}" +
807 ",between:{count:2,x:-2.0, ny:{count:1}}" +
808 " } }"
809 );
810
811
812
813 // stats at top level
814 client.testJQ(params(p, "q", "*:*"
815 , "json.facet", "{ sum1:'sum(${num_d})', sumsq1:'sumsq(${num_d})', avg1:'avg(${num_d})', avg2:'avg(def(${num_d},0))', min1:'min(${num_d})', max1:'max(${num_d})'" +
816 ", numwhere:'unique(${where_s})', unique_num_i:'unique(${num_i})', unique_num_d:'unique(${num_d})', unique_date:'unique(${date})'" +
817 ", where_hll:'hll(${where_s})', hll_num_i:'hll(${num_i})', hll_num_d:'hll(${num_d})', hll_date:'hll(${date})'" +
818 ", med:'percentile(${num_d},50)', perc:'percentile(${num_d},0,50.0,100)' }"
819 )
820 , "facets=={ 'count':6, " +
821 "sum1:3.0, sumsq1:247.0, avg1:0.6, avg2:0.5, min1:-9.0, max1:11.0" +
822 ", numwhere:2, unique_num_i:4, unique_num_d:5, unique_date:5" +
823 ", where_hll:2, hll_num_i:4, hll_num_d:5, hll_date:5" +
824 ", med:2.0, perc:[-9.0,2.0,11.0] }"
825 );
826
827 // stats at top level, no matches
828 client.testJQ(params(p, "q", "id:DOESNOTEXIST"
829 , "json.facet", "{ sum1:'sum(${num_d})', sumsq1:'sumsq(${num_d})', avg1:'avg(${num_d})', min1:'min(${num_d})', max1:'max(${num_d})'" +
830 ", numwhere:'unique(${where_s})', unique_num_i:'unique(${num_i})', unique_num_d:'unique(${num_d})', unique_date:'unique(${date})'" +
831 ", where_hll:'hll(${where_s})', hll_num_i:'hll(${num_i})', hll_num_d:'hll(${num_d})', hll_date:'hll(${date})'" +
832 ", med:'percentile(${num_d},50)', perc:'percentile(${num_d},0,50.0,100)' }"
833 )
834 , "facets=={count:0 " +
835 "/* ,sum1:0.0, sumsq1:0.0, avg1:0.0, min1:'NaN', max1:'NaN', numwhere:0 */" +
836 " }"
837 );
838
839 //
840 // tests on a multi-valued field with actual multiple values, just to ensure that we are
841 // using a multi-valued method for the rest of the tests when appropriate.
842 //
843
844 client.testJQ(params(p, "q", "*:*"
845 , "json.facet", "{cat:{terms:{${terms} field:'${multi_ss}', facet:{nj:{query:'${where_s}:NJ'}} } }} }"
846 )
847 , "facets=={ 'count':6, " +
848 "'cat':{ 'buckets':[{ 'val':'a', 'count':3, 'nj':{ 'count':2}}, { 'val':'b', 'count':3, 'nj':{ 'count':2}}]} }"
849 );
850
851 // test unique on multi-valued field
852 client.testJQ(params(p, "q", "*:*"
853 , "json.facet", "{" +
854 "x:'unique(${multi_ss})'" +
855 ",y:{query:{q:'id:2', facet:{x:'unique(${multi_ss})'} }} " +
856 ",x2:'hll(${multi_ss})'" +
857 ",y2:{query:{q:'id:2', facet:{x:'hll(${multi_ss})'} }} " +
858
859 " }"
860 )
861 , "facets=={count:6 " +
862 ",x:2" +
863 ",y:{count:1, x:2}" + // single document should yield 2 unique values
864 ",x2:2" +
865 ",y2:{count:1, x:2}" + // single document should yield 2 unique values
866 " }"
867 );
868
869 // test allBucket multi-valued
870 client.testJQ(params(p, "q", "*:*"
871 , "json.facet", "{x:{terms:{${terms} field:'${multi_ss}',allBuckets:true}}}"
872 )
873 , "facets=={ count:6, " +
874 "x:{ buckets:[{val:a, count:3}, {val:b, count:3}] , allBuckets:{count:6} } }"
875 );
876
877 // allBuckets for multi-valued field with stats. This can sometimes take a different path of adding complete DocSets to the Acc
878 // also test limit:0
879 client.testJQ(params(p, "q", "*:*"
880 , "json.facet", "{" +
881 " f0:{${terms} type:terms, field:${multi_ss}, allBuckets:true, limit:0} " +
882 ",f1:{${terms} type:terms, field:${multi_ss}, allBuckets:true, limit:0, offset:1} " + // offset with 0 limit
883 ",f2:{${terms} type:terms, field:${multi_ss}, allBuckets:true, limit:0, facet:{x:'sum(${num_d})'}, sort:'x desc' } " +
884 ",f3:{${terms} type:terms, field:${multi_ss}, allBuckets:true, limit:0, missing:true, facet:{x:'sum(${num_d})', y:'avg(${num_d})'}, sort:'x desc' } " +
885 "}"
886 )
887 , "facets=={ 'count':6, " +
888 " f0:{allBuckets:{count:6}, buckets:[]}" +
889 ",f1:{allBuckets:{count:6}, buckets:[]}" +
890 ",f2:{allBuckets:{count:6, x:-15.0}, buckets:[]} " +
891 ",f3:{allBuckets:{count:6, x:-15.0, y:-2.5}, buckets:[], missing:{count:2, x:4.0, y:4.0} }} " +
892 "}"
893 );
894
895 // allBuckets with numeric field with stats.
896 // also test limit:0
897 client.testJQ(params(p, "q", "*:*"
898 , "json.facet", "{" +
899 " f0:{${terms} type:terms, field:${num_i}, allBuckets:true, limit:0} " +
900 ",f1:{${terms} type:terms, field:${num_i}, allBuckets:true, limit:0, offset:1} " + // offset with 0 limit
901 ",f2:{${terms} type:terms, field:${num_i}, allBuckets:true, limit:0, facet:{x:'sum(${num_d})'}, sort:'x desc' } " +
902 "}"
903 )
904 , "facets=={ 'count':6, " +
905 " f0:{allBuckets:{count:5}, buckets:[]}" +
906 ",f1:{allBuckets:{count:5}, buckets:[]}" +
907 ",f2:{allBuckets:{count:5, x:3.0}, buckets:[]} " +
908 "}"
909 );
910
911
912 //////////////////////////////////////////////////////////////////////////////////////////////////////////
913 // test converting legacy facets
914
915 // test mincount
916 client.testJQ(params(p, "q", "*:*"
917 // , "json.facet", "{f1:{terms:{field:'${cat_s}', mincount:3}}}"
918 , "facet","true", "facet.version", "2", "facet.field","{!key=f1}${cat_s}", "facet.mincount","3"
919 )
920 , "facets=={ 'count':6, " +
921 "'f1':{ 'buckets':[{ 'val':'B', 'count':3}]} } "
922 );
923
924 // test prefix
925 client.testJQ(params(p, "q", "*:*"
926 // , "json.facet", "{f1:{terms:{field:${super_s}, prefix:s, mincount:0 }}}" // even with mincount=0, we should only see buckets with the prefix
927 , "facet","true", "facet.version", "2", "facet.field","{!key=f1}${super_s}", "facet.prefix","s", "facet.mincount","0"
928 )
929 , "facets=={ 'count':6, " +
930 "'f1':{ 'buckets':[{val:spiderman, count:1}, {val:superman, count:1}]} } "
931 );
932
933 // range facet with sub facets and stats
934 client.testJQ(params(p, "q", "*:*"
935 // , "json.facet", "{f:{range:{field:${num_d}, start:-5, end:10, gap:5, facet:{ x:'sum(${num_i})', ny:{query:'${where_s}:NY'}} }}}"
936 , "facet","true", "facet.version", "2", "facet.range","{!key=f}${num_d}", "facet.range.start","-5", "facet.range.end","10", "facet.range.gap","5"
937 , "f.f.facet.stat","x:sum(${num_i})", "subfacet.f.query","{!key=ny}${where_s}:NY"
938
939 )
940 , "facets=={count:6, f:{buckets:[ {val:-5.0,count:1,x:-5.0,ny:{count:1}}, {val:0.0,count:2,x:5.0,ny:{count:1}}, {val:5.0,count:0 /* ,x:0.0,ny:{count:0} */ } ] } }"
941 );
942
943 // test sorting by stat
944 client.testJQ(params(p, "q", "*:*"
945 // , "json.facet", "{f1:{terms:{field:'${cat_s}', sort:'n1 desc', facet:{n1:'sum(${num_d})'} }}" +
946 // " , f2:{terms:{field:'${cat_s}', sort:'n1 asc', facet:{n1:'sum(${num_d})'} }} }"
947 , "facet","true", "facet.version", "2", "facet.field","{!key=f1}${cat_s}", "f.f1.facet.sort","n1 desc", "facet.stat","n1:sum(${num_d})"
948 , "facet.field","{!key=f2}${cat_s}", "f.f1.facet.sort","n1 asc"
949 )
950 , "facets=={ 'count':6, " +
951 " f1:{ 'buckets':[{ val:'A', count:2, n1:6.0 }, { val:'B', count:3, n1:-3.0}]}" +
952 ", f2:{ 'buckets':[{ val:'B', count:3, n1:-3.0}, { val:'A', count:2, n1:6.0 }]} }"
953 );
954
955 // range facet with sub facets and stats, with "other:all", on subset
956 client.testJQ(params(p, "q", "id:(3 4 6)"
957 //, "json.facet", "{f:{range:{field:${num_d}, start:-5, end:10, gap:5, other:all, facet:{ x:'sum(${num_i})', ny:{query:'${where_s}:NY'}} }}}"
958 , "facet","true", "facet.version", "2", "facet.range","{!key=f}${num_d}", "facet.range.start","-5", "facet.range.end","10", "facet.range.gap","5"
959 , "f.f.facet.stat","x:sum(${num_i})", "subfacet.f.query","{!key=ny}${where_s}:NY", "facet.range.other","all"
960 )
961 , "facets=={count:3, f:{buckets:[ {val:-5.0,count:1,x:-5.0,ny:{count:1}}, {val:0.0,count:1,x:3.0,ny:{count:0}}, {val:5.0,count:0 /* ,x:0.0,ny:{count:0} */} ]" +
962 ",before: {count:0 /* ,x:0.0,ny:{count:0} */ }" +
963 ",after: {count:0 /* ,x:0.0,ny:{count:0} */}" +
964 ",between:{count:2,x:-2.0, ny:{count:1}}" +
965 " } }"
966 );
967
968
969 ////////////////////////////////////////////////////////////////////////////////////////////
970 // multi-select / exclude tagged filters via excludeTags
971 ////////////////////////////////////////////////////////////////////////////////////////////
972
973 // test uncached multi-select (see SOLR-8496)
974 client.testJQ(params(p, "q", "{!cache=false}*:*", "fq","{!tag=doc3,allfilt}-id:3"
975
976 , "json.facet", "{" +
977 "f1:{${terms} type:terms, field:${cat_s}, domain:{excludeTags:doc3} } " +
978 "}"
979 )
980 , "facets=={ count:5, " +
981 " f1:{ buckets:[ {val:B, count:3}, {val:A, count:2} ] }" +
982 "}"
983 );
984
985 // nested query facets on subset (with excludeTags)
986 client.testJQ(params(p, "q", "*:*", "fq","{!tag=abc}id:(2 3)"
987 , "json.facet", "{ processEmpty:true," +
988 " f1:{query:{q:'${cat_s}:B', facet:{nj:{query:'${where_s}:NJ'}, ny:{query:'${where_s}:NY'}} , excludeTags:[xyz,qaz]}}" +
989 ",f2:{query:{q:'${cat_s}:B', facet:{nj:{query:'${where_s}:NJ'}, ny:{query:'${where_s}:NY'}} , excludeTags:abc }}" +
990 ",f3:{query:{q:'${cat_s}:B', facet:{nj:{query:'${where_s}:NJ'}, ny:{query:'${where_s}:NY'}} , excludeTags:'xyz,abc,qaz' }}" +
991 ",f4:{query:{q:'${cat_s}:B', facet:{nj:{query:'${where_s}:NJ'}, ny:{query:'${where_s}:NY'}} , excludeTags:[xyz , abc , qaz] }}" +
992 ",f5:{query:{q:'${cat_s}:B', facet:{nj:{query:'${where_s}:NJ'}, ny:{query:'${where_s}:NY'}} , excludeTags:[xyz,qaz]}}" + // this is repeated, but it did fail when a single context was shared among sub-facets
993 ",f6:{query:{q:'${cat_s}:B', facet:{processEmpty:true, nj:{query:'${where_s}:NJ'}, ny:{ type:query, q:'${where_s}:NY', excludeTags:abc}} }}" + // exclude in a sub-facet
994 ",f7:{query:{q:'${cat_s}:B', facet:{processEmpty:true, nj:{query:'${where_s}:NJ'}, ny:{ type:query, q:'${where_s}:NY', excludeTags:xyz}} }}" + // exclude in a sub-facet that doesn't match
995 "}"
996 )
997 , "facets=={ 'count':2, " +
998 " 'f1':{'count':1, 'nj':{'count':1}, 'ny':{'count':0}}" +
999 ",'f2':{'count':3, 'nj':{'count':2}, 'ny':{'count':1}}" +
1000 ",'f3':{'count':3, 'nj':{'count':2}, 'ny':{'count':1}}" +
1001 ",'f4':{'count':3, 'nj':{'count':2}, 'ny':{'count':1}}" +
1002 ",'f5':{'count':1, 'nj':{'count':1}, 'ny':{'count':0}}" +
1003 ",'f6':{'count':1, 'nj':{'count':1}, 'ny':{'count':1}}" +
1004 ",'f7':{'count':1, 'nj':{'count':1}, 'ny':{'count':0}}" +
1005 "}"
1006 );
1007
1008 // terms facet with nested query facet (with excludeTags, using new format inside domain:{})
1009 client.testJQ(params(p, "q", "{!cache=false}*:*", "fq", "{!tag=doc6,allfilt}-id:6", "fq","{!tag=doc3,allfilt}-id:3"
1010
1011 , "json.facet", "{processEmpty:true, " +
1012 " f0:{${terms} type:terms, field:${cat_s}, facet:{nj:{query:'${where_s}:NJ'}} } " +
1013 ",f1:{${terms} type:terms, field:${cat_s}, domain:{excludeTags:doc3}, missing:true, facet:{nj:{query:'${where_s}:NJ'}} } " +
1014 ",f2:{${terms} type:terms, field:${cat_s}, domain:{excludeTags:allfilt},missing:true, facet:{nj:{query:'${where_s}:NJ'}} } " +
1015 ",f3:{${terms} type:terms, field:${cat_s}, domain:{excludeTags:doc6}, missing:true, facet:{nj:{query:'${where_s}:NJ'}} } " +
1016 "}"
1017 )
1018 , "facets=={ count:4, " +
1019 " f0:{ buckets:[ {val:A, count:2, nj:{ count:1}}, {val:B, count:2, nj:{count:2}} ] }" +
1020 ",f1:{ buckets:[ {val:A, count:2, nj:{ count:1}}, {val:B, count:2, nj:{count:2}} ] , missing:{count:1,nj:{count:0}} }" +
1021 ",f2:{ buckets:[ {val:B, count:3, nj:{ count:2}}, {val:A, count:2, nj:{count:1}} ] , missing:{count:1,nj:{count:0}} }" +
1022 ",f3:{ buckets:[ {val:B, count:3, nj:{ count:2}}, {val:A, count:2, nj:{count:1}} ] , missing:{count:0} }" +
1023 "}"
1024 );
1025
1026 // range facet with sub facets and stats, with "other:all" (with excludeTags)
1027 client.testJQ(params(p, "q", "*:*", "fq", "{!tag=doc6,allfilt}-id:6", "fq","{!tag=doc3,allfilt}-id:3"
1028 , "json.facet", "{processEmpty:true " +
1029 ", f1:{type:range, field:${num_d}, start:-5, end:10, gap:5, other:all, facet:{ x:'sum(${num_i})', ny:{query:'${where_s}:NY'}} , domain:{excludeTags:allfilt} }" +
1030 ", f2:{type:range, field:${num_d}, start:-5, end:10, gap:5, other:all, facet:{ x:'sum(${num_i})', ny:{query:'${where_s}:NY'}} }" +
1031 "}"
1032 )
1033 , "facets=={count:4" +
1034 ",f1:{buckets:[ {val:-5.0,count:1,x:-5.0,ny:{count:1}}, {val:0.0,count:2,x:5.0,ny:{count:1}}, {val:5.0,count:0} ]" +
1035 ",before: {count:1,x:-5.0,ny:{count:0}}" +
1036 ",after: {count:1,x:7.0, ny:{count:0}}" +
1037 ",between:{count:3,x:0.0, ny:{count:2}} }" +
1038 ",f2:{buckets:[ {val:-5.0,count:0}, {val:0.0,count:2,x:5.0,ny:{count:1}}, {val:5.0,count:0} ]" +
1039 ",before: {count:1,x:-5.0,ny:{count:0}}" +
1040 ",after: {count:1,x:7.0, ny:{count:0}}" +
1041 ",between:{count:2,x:5.0, ny:{count:1}} }" +
1042 "}"
1043 );
1044
1045
1046 //
1047 // facet on numbers
1048 //
1049 client.testJQ(params(p, "q", "*:*"
1050 , "json.facet", "{" +
1051 " f1:{${terms} type:field, field:${num_i} }" +
1052 ",f2:{${terms} type:field, field:${num_i}, sort:'count asc' }" +
1053 ",f3:{${terms} type:field, field:${num_i}, sort:'index asc' }" +
1054 ",f4:{${terms} type:field, field:${num_i}, sort:'index desc' }" +
1055 ",f5:{${terms} type:field, field:${num_i}, sort:'index desc', limit:1, missing:true, allBuckets:true, numBuckets:true }" +
1056 ",f6:{${terms} type:field, field:${num_i}, sort:'index desc', mincount:2, numBuckets:true }" + // mincount should lower numbuckets
1057 ",f7:{${terms} type:field, field:${num_i}, sort:'index desc', offset:2, numBuckets:true }" + // test offset
1058 ",f8:{${terms} type:field, field:${num_i}, sort:'index desc', offset:100, numBuckets:true }" + // test high offset
1059 ",f9:{${terms} type:field, field:${num_i}, sort:'x desc', facet:{x:'avg(${num_d})'}, missing:true, allBuckets:true, numBuckets:true }" + // test stats
1060 ",f10:{${terms} type:field, field:${num_i}, facet:{a:{query:'${cat_s}:A'}}, missing:true, allBuckets:true, numBuckets:true }" + // test subfacets
1061 ",f11:{${terms} type:field, field:${num_i}, facet:{a:'unique(${num_d})'} ,missing:true, allBuckets:true, sort:'a desc' }" + // test subfacet using unique on numeric field (this previously triggered a resizing bug)
1062 "}"
1063 )
1064 , "facets=={count:6 " +
1065 ",f1:{ buckets:[{val:-5,count:2},{val:2,count:1},{val:3,count:1},{val:7,count:1} ] } " +
1066 ",f2:{ buckets:[{val:2,count:1},{val:3,count:1},{val:7,count:1},{val:-5,count:2} ] } " +
1067 ",f3:{ buckets:[{val:-5,count:2},{val:2,count:1},{val:3,count:1},{val:7,count:1} ] } " +
1068 ",f4:{ buckets:[{val:7,count:1},{val:3,count:1},{val:2,count:1},{val:-5,count:2} ] } " +
1069 ",f5:{ buckets:[{val:7,count:1}] , numBuckets:4, allBuckets:{count:5}, missing:{count:1} } " +
1070 ",f6:{ buckets:[{val:-5,count:2}] , numBuckets:1 } " +
1071 ",f7:{ buckets:[{val:2,count:1},{val:-5,count:2}] , numBuckets:4 } " +
1072 ",f8:{ buckets:[] , numBuckets:4 } " +
1073 ",f9:{ buckets:[{val:7,count:1,x:11.0},{val:2,count:1,x:4.0},{val:3,count:1,x:2.0},{val:-5,count:2,x:-7.0} ], numBuckets:4, allBuckets:{count:5,x:0.6},missing:{count:1,x:0.0} } " + // TODO: should missing exclude "x" because no values were collected?
1074 ",f10:{ buckets:[{val:-5,count:2,a:{count:0}},{val:2,count:1,a:{count:1}},{val:3,count:1,a:{count:1}},{val:7,count:1,a:{count:0}} ], numBuckets:4, allBuckets:{count:5},missing:{count:1,a:{count:0}} } " +
1075 ",f11:{ buckets:[{val:-5,count:2,a:2},{val:2,count:1,a:1},{val:3,count:1,a:1},{val:7,count:1,a:1} ] , missing:{count:1,a:0} , allBuckets:{count:5,a:5} } " +
1076 "}"
1077 );
1078
1079
1080 // facet on a float field - shares same code with integers/longs currently, so we only need to test labels/sorting
1081 client.testJQ(params(p, "q", "*:*"
1082 , "json.facet", "{" +
1083 " f1:{${terms} type:field, field:${num_d} }" +
1084 ",f2:{${terms} type:field, field:${num_d}, sort:'index desc' }" +
1085 "}"
1086 )
1087 , "facets=={count:6 " +
1088 ",f1:{ buckets:[{val:-9.0,count:1},{val:-5.0,count:1},{val:2.0,count:1},{val:4.0,count:1},{val:11.0,count:1} ] } " +
1089 ",f2:{ buckets:[{val:11.0,count:1},{val:4.0,count:1},{val:2.0,count:1},{val:-5.0,count:1},{val:-9.0,count:1} ] } " +
1090 "}"
1091 );
1092
1093 // test 0, min/max int
1094 client.testJQ(params(p, "q", "*:*"
1095 , "json.facet", "{" +
1096 " u : 'unique(${Z_num_i})'" +
1097 ", f1:{${terms} type:field, field:${Z_num_i} }" +
1098 "}"
1099 )
1100 , "facets=={count:6 " +
1101 ",u:3" +
1102 ",f1:{ buckets:[{val:" + Integer.MIN_VALUE + ",count:1},{val:0,count:1},{val:" + Integer.MAX_VALUE+",count:1}]} " +
1103 "}"
1104 );
1105
1106
1107
1108 // multi-valued integer
1109 client.testJQ(params(p, "q", "*:*"
1110 , "json.facet", "{ " +
1111 " c1:'unique(${num_is})', c2:'hll(${num_is})'" +
1112 ",f1:{${terms} type:terms, field:${num_is} } " +
1113 "}"
1114 )
1115 , "facets=={ count:6 " +
1116 ", c1:5, c2:5" +
1117 ", f1:{ buckets:[ {val:-1,count:2},{val:0,count:2},{val:3,count:2},{val:-5,count:1},{val:2,count:1} ] } " +
1118 "} "
1119 );
1120
1121 // multi-valued float
1122 client.testJQ(params(p, "q", "*:*"
1123 , "json.facet", "{ " +
1124 " c1:'unique(${num_fs})', c2:'hll(${num_fs})'" +
1125 ",f1:{${terms} type:terms, field:${num_fs} } " +
1126 "}"
1127 )
1128 , "facets=={ count:6 " +
1129 ", c1:5, c2:5" +
1130 ", f1:{ buckets:[ {val:-1.5,count:2},{val:0.0,count:2},{val:3.0,count:2},{val:-5.0,count:1},{val:2.0,count:1} ] } " +
1131 "} "
1132 );
1133
1134
1135 }
1136
1137
1138
1139
1140 @Test
1141 public void testBigger() throws Exception {
1142 ModifiableSolrParams p = params("rows", "0", "cat_s", "cat_ss", "where_s", "where_ss");
1143 // doBigger(Client.localClient, p);
1144
1145 initServers();
1146 Client client = servers.getClient(random().nextInt());
1147 client.queryDefaults().set( "shards", servers.getShards() );
1148 doBigger( client, p );
1149 }
1150
1151 public void doBigger(Client client, ModifiableSolrParams p) throws Exception {
1152 MacroExpander m = new MacroExpander(p.getMap());
1153
1154 String cat_s = m.expand("${cat_s}");
1155 String where_s = m.expand("${where_s}");
1156
1157 client.deleteByQuery("*:*", null);
1158
1159 Random r = new Random(0); // make deterministic
1160 int numCat = 1;
1161 int numWhere = 2000000000;
1162 int commitPercent = 10;
1163 int ndocs=1000;
1164
1165 Map<Integer, Map<Integer, List<Integer>>> model = new HashMap(); // cat->where->list<ids>
1166 for (int i=0; i<ndocs; i++) {
1167 Integer cat = r.nextInt(numCat);
1168 Integer where = r.nextInt(numWhere);
1169 client.add( sdoc("id", i, cat_s,cat, where_s, where) , null );
1170 Map<Integer,List<Integer>> sub = model.get(cat);
1171 if (sub == null) {
1172 sub = new HashMap<>();
1173 model.put(cat, sub);
1174 }
1175 List<Integer> ids = sub.get(where);
1176 if (ids == null) {
1177 ids = new ArrayList<>();
1178 sub.put(where, ids);
1179 }
1180 ids.add(i);
1181
1182 if (r.nextInt(100) < commitPercent) {
1183 client.commit();
1184 }
1185 }
1186
1187 client.commit();
1188
1189 int sz = model.get(0).size();
1190
1191 client.testJQ(params(p, "q", "*:*"
1192 , "json.facet", "{f1:{type:terms, field:${cat_s}, limit:2, facet:{x:'unique($where_s)'} }}"
1193 )
1194 , "facets=={ 'count':" + ndocs + "," +
1195 "'f1':{ 'buckets':[{ 'val':'0', 'count':" + ndocs + ", x:" + sz + " }]} } "
1196 );
1197
1198 if (client.local()) {
1199 // distrib estimation prob won't match
1200 client.testJQ(params(p, "q", "*:*"
1201 , "json.facet", "{f1:{type:terms, field:${cat_s}, limit:2, facet:{x:'hll($where_s)'} }}"
1202 )
1203 , "facets=={ 'count':" + ndocs + "," +
1204 "'f1':{ 'buckets':[{ 'val':'0', 'count':" + ndocs + ", x:" + sz + " }]} } "
1205 );
1206 }
1207
1208 }
1209
1210 public void testTolerant() throws Exception {
1211 initServers();
1212 Client client = servers.getClient(random().nextInt());
1213 client.queryDefaults().set("shards", servers.getShards() + ",[ff01::114]:33332:/ignore_exception");
1214 indexSimple(client);
1215
1216 try {
1217 client.testJQ(params("ignore_exception", "true", "shards.tolerant", "false", "q", "*:*"
1218 , "json.facet", "{f:{type:terms, field:cat_s}}"
1219 )
1220 , "facets=={ count:6," +
1221 "f:{ buckets:[{val:B,count:3},{val:A,count:2}] }" +
1222 "}"
1223 );
1224 fail("we should have failed");
1225 } catch (Exception e) {
1226 // ok
1227 }
1228
1229 client.testJQ(params("ignore_exception", "true", "shards.tolerant", "true", "q", "*:*"
1230 , "json.facet", "{f:{type:terms, field:cat_s}}"
1231 )
1232 , "facets=={ count:6," +
1233 "f:{ buckets:[{val:B,count:3},{val:A,count:2}] }" +
1234 "}"
1235 );
1236 }
1237
1238
1239
1240 @Test
1241 public void testBlockJoin() throws Exception {
1242 doBlockJoin(Client.localClient());
1243 }
1244
1245 public void doBlockJoin(Client client) throws Exception {
1246 ModifiableSolrParams p = params("rows","0");
1247
1248 client.deleteByQuery("*:*", null);
1249
1250 SolrInputDocument parent;
1251 parent = sdoc("id", "1", "type_s","book", "book_s","A", "v_t","q");
1252 client.add(parent, null);
1253
1254 parent = sdoc("id", "2", "type_s","book", "book_s","B", "v_t","q w");
1255 parent.addChildDocument( sdoc("id","2.1", "type_s","page", "page_s","a", "v_t","x y z") );
1256 parent.addChildDocument( sdoc("id","2.2", "type_s","page", "page_s","b", "v_t","x y ") );
1257 parent.addChildDocument( sdoc("id","2.3", "type_s","page", "page_s","c", "v_t"," y z" ) );
1258 client.add(parent, null);
1259
1260 parent = sdoc("id", "3", "type_s","book", "book_s","C", "v_t","q w e");
1261 parent.addChildDocument( sdoc("id","3.1", "type_s","page", "page_s","d", "v_t","x ") );
1262 parent.addChildDocument( sdoc("id","3.2", "type_s","page", "page_s","e", "v_t"," y ") );
1263 parent.addChildDocument( sdoc("id","3.3", "type_s","page", "page_s","f", "v_t"," z") );
1264 client.add(parent, null);
1265
1266 parent = sdoc("id", "4", "type_s","book", "book_s","D", "v_t","e");
1267 client.add(parent, null);
1268
1269 client.commit();
1270
1271 client.testJQ(params(p, "q", "*:*"
1272 , "json.facet", "{ " +
1273 "pages:{ type:query, domain:{blockChildren:'type_s:book'} , facet:{ x:{field:v_t} } }" +
1274 ",pages2:{type:terms, field:v_t, domain:{blockChildren:'type_s:book'} }" +
1275 ",books:{ type:query, domain:{blockParent:'type_s:book'} , facet:{ x:{field:v_t} } }" +
1276 ",books2:{type:terms, field:v_t, domain:{blockParent:'type_s:book'} }" +
1277 ",pageof3:{ type:query, q:'id:3', facet : { x : { type:terms, field:page_s, domain:{blockChildren:'type_s:book'}}} }" +
1278 ",bookof22:{ type:query, q:'id:2.2', facet : { x : { type:terms, field:book_s, domain:{blockParent:'type_s:book'}}} }" +
1279 ",missing_blockParent:{ type:query, domain:{blockParent:'type_s:does_not_exist'} }" +
1280 ",missing_blockChildren:{ type:query, domain:{blockChildren:'type_s:does_not_exist'} }" +
1281 "}"
1282 )
1283 , "facets=={ count:10" +
1284 ", pages:{count:6 , x:{buckets:[ {val:y,count:4},{val:x,count:3},{val:z,count:3} ]} }" +
1285 ", pages2:{ buckets:[ {val:y,count:4},{val:x,count:3},{val:z,count:3} ] }" +
1286 ", books:{count:4 , x:{buckets:[ {val:q,count:3},{val:e,count:2},{val:w,count:2} ]} }" +
1287 ", books2:{ buckets:[ {val:q,count:3},{val:e,count:2},{val:w,count:2} ] }" +
1288 ", pageof3:{count:1 , x:{buckets:[ {val:d,count:1},{val:e,count:1},{val:f,count:1} ]} }" +
1289 ", bookof22:{count:1 , x:{buckets:[ {val:B,count:1} ]} }" +
1290 ", missing_blockParent:{count:0}" +
1291 ", missing_blockChildren:{count:0}" +
1292 "}"
1293 );
1294
1295 // no matches in base query
1296 client.testJQ(params("q", "no_match_s:NO_MATCHES"
1297 , "json.facet", "{ processEmpty:true," +
1298 "pages:{ type:query, domain:{blockChildren:'type_s:book'} }" +
1299 ",books:{ type:query, domain:{blockParent:'type_s:book'} }" +
1300 "}"
1301 )
1302 , "facets=={ count:0" +
1303 ", pages:{count:0}" +
1304 ", books:{count:0}" +
1305 "}"
1306 );
1307
1308
1309 // test facet on children nested under terms facet on parents
1310 client.testJQ(params("q", "*:*"
1311 , "json.facet", "{" +
1312 "books:{ type:terms, field:book_s, facet:{ pages:{type:terms, field:v_t, domain:{blockChildren:'type_s:book'}} } }" +
1313 "}"
1314 )
1315 , "facets=={ count:10" +
1316 ", books:{buckets:[{val:A,count:1,pages:{buckets:[]}}" +
1317 " ,{val:B,count:1,pages:{buckets:[{val:y,count:3},{val:x,count:2},{val:z,count:2}]}}" +
1318 " ,{val:C,count:1,pages:{buckets:[{val:x,count:1},{val:y,count:1},{val:z,count:1}]}}" +
1319 " ,{val:D,count:1,pages:{buckets:[]}}"+
1320 "] }" +
1321 "}"
1322 );
1323
1324
1325 }
1326
1327
1328 @Test
1329 public void testErrors() throws Exception {
1330 doTestErrors(Client.localClient());
1331 }
1332
1333 public void doTestErrors(Client client) throws Exception {
1334 ModifiableSolrParams p = params("rows", "0");
1335 client.deleteByQuery("*:*", null);
1336
1337 try {
1338 client.testJQ(params("ignore_exception", "true", "q", "*:*"
1339 , "json.facet", "{f:{type:ignore_exception_aaa, field:bbbbbb}}"
1340 )
1341 );
1342 } catch (SolrException e) {
1343 assertTrue( e.getMessage().contains("ignore_exception_aaa") );
1344 }
1345
1346 }
1347
1348
1349
1350 public void XtestPercentiles() {
1351 AVLTreeDigest catA = new AVLTreeDigest(100);
1352 catA.add(4);
1353 catA.add(2);
1354
1355 AVLTreeDigest catB = new AVLTreeDigest(100);
1356 catB.add(-9);
1357 catB.add(11);
1358 catB.add(-5);
1359
1360 AVLTreeDigest all = new AVLTreeDigest(100);
1361 all.add(catA);
1362 all.add(catB);
1363
1364 System.out.println(str(catA));
1365 System.out.println(str(catB));
1366 System.out.println(str(all));
1367
1368 // 2.0 2.2 3.0 3.8 4.0
1369 // -9.0 -8.2 -5.0 7.800000000000001 11.0
1370 // -9.0 -7.3999999999999995 2.0 8.200000000000001 11.0
1371 }
1372
1373 private static String str(AVLTreeDigest digest) {
1374 StringBuilder sb = new StringBuilder();
1375 for (double d : new double[] {0,.1,.5,.9,1}) {
1376 sb.append(" ").append(digest.quantile(d));
1377 }
1378 return sb.toString();
1379 }
1380
1381 /*** test code to ensure TDigest is working as we expect. */
1382
1383 public void XtestTDigest() throws Exception {
1384 AVLTreeDigest t1 = new AVLTreeDigest(100);
1385 t1.add(10, 1);
1386 t1.add(90, 1);
1387 t1.add(50, 1);
1388
1389 System.out.println(t1.quantile(0.1));
1390 System.out.println(t1.quantile(0.5));
1391 System.out.println(t1.quantile(0.9));
1392
1393 assertEquals(t1.quantile(0.5), 50.0, 0.01);
1394
1395 AVLTreeDigest t2 = new AVLTreeDigest(100);
1396 t2.add(130, 1);
1397 t2.add(170, 1);
1398 t2.add(90, 1);
1399
1400 System.out.println(t2.quantile(0.1));
1401 System.out.println(t2.quantile(0.5));
1402 System.out.println(t2.quantile(0.9));
1403
1404 AVLTreeDigest top = new AVLTreeDigest(100);
1405
1406 t1.compress();
1407 ByteBuffer buf = ByteBuffer.allocate(t1.byteSize()); // upper bound
1408 t1.asSmallBytes(buf);
1409 byte[] arr1 = Arrays.copyOf(buf.array(), buf.position());
1410
1411 ByteBuffer rbuf = ByteBuffer.wrap(arr1);
1412 top.add(AVLTreeDigest.fromBytes(rbuf));
1413
1414 System.out.println(top.quantile(0.1));
1415 System.out.println(top.quantile(0.5));
1416 System.out.println(top.quantile(0.9));
1417
1418 t2.compress();
1419 ByteBuffer buf2 = ByteBuffer.allocate(t2.byteSize()); // upper bound
1420 t2.asSmallBytes(buf2);
1421 byte[] arr2 = Arrays.copyOf(buf2.array(), buf2.position());
1422
1423 ByteBuffer rbuf2 = ByteBuffer.wrap(arr2);
1424 top.add(AVLTreeDigest.fromBytes(rbuf2));
1425
1426 System.out.println(top.quantile(0.1));
1427 System.out.println(top.quantile(0.5));
1428 System.out.println(top.quantile(0.9));
1429 }
1430
1431 public void XtestHLL() {
1432 HLLAgg.HLLFactory fac = new HLLAgg.HLLFactory();
1433 HLL hll = fac.getHLL();
1434 hll.addRaw(123456789);
1435 hll.addRaw(987654321);
1436 }
1437
1438 }