|
20 | 20 | package org.elasticsearch.search.aggregations.metrics;
|
21 | 21 |
|
22 | 22 | import org.apache.lucene.document.BinaryDocValuesField;
|
| 23 | +import org.apache.lucene.document.Document; |
| 24 | +import org.apache.lucene.document.DoubleDocValuesField; |
23 | 25 | import org.apache.lucene.document.IntPoint;
|
| 26 | +import org.apache.lucene.document.LatLonDocValuesField; |
24 | 27 | import org.apache.lucene.document.NumericDocValuesField;
|
25 | 28 | import org.apache.lucene.document.SortedDocValuesField;
|
26 | 29 | import org.apache.lucene.document.SortedNumericDocValuesField;
|
| 30 | +import org.apache.lucene.document.SortedSetDocValuesField; |
27 | 31 | import org.apache.lucene.index.DirectoryReader;
|
28 | 32 | import org.apache.lucene.index.IndexReader;
|
29 | 33 | import org.apache.lucene.index.RandomIndexWriter;
|
|
35 | 39 | import org.apache.lucene.util.BytesRef;
|
36 | 40 | import org.elasticsearch.common.CheckedConsumer;
|
37 | 41 | import org.elasticsearch.common.geo.GeoPoint;
|
| 42 | +import org.elasticsearch.common.settings.Settings; |
38 | 43 | import org.elasticsearch.index.mapper.BooleanFieldMapper;
|
39 | 44 | import org.elasticsearch.index.mapper.DateFieldMapper;
|
40 | 45 | import org.elasticsearch.index.mapper.GeoPointFieldMapper;
|
|
44 | 49 | import org.elasticsearch.index.mapper.NumberFieldMapper;
|
45 | 50 | import org.elasticsearch.index.mapper.RangeFieldMapper;
|
46 | 51 | import org.elasticsearch.index.mapper.RangeType;
|
| 52 | +import org.elasticsearch.script.MockScriptEngine; |
| 53 | +import org.elasticsearch.script.Script; |
| 54 | +import org.elasticsearch.script.ScriptEngine; |
| 55 | +import org.elasticsearch.script.ScriptModule; |
| 56 | +import org.elasticsearch.script.ScriptService; |
| 57 | +import org.elasticsearch.script.ScriptType; |
| 58 | +import org.elasticsearch.search.aggregations.AggregationBuilder; |
47 | 59 | import org.elasticsearch.search.aggregations.AggregatorTestCase;
|
48 | 60 | import org.elasticsearch.search.aggregations.support.AggregationInspectionHelper;
|
| 61 | +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; |
49 | 62 | import org.elasticsearch.search.aggregations.support.ValueType;
|
| 63 | +import org.elasticsearch.search.aggregations.support.ValuesSourceType; |
50 | 64 |
|
51 | 65 | import java.io.IOException;
|
52 | 66 | import java.util.Arrays;
|
| 67 | +import java.util.Collections; |
| 68 | +import java.util.HashMap; |
53 | 69 | import java.util.HashSet;
|
| 70 | +import java.util.List; |
| 71 | +import java.util.Map; |
54 | 72 | import java.util.Set;
|
55 | 73 | import java.util.function.Consumer;
|
| 74 | +import java.util.function.Function; |
56 | 75 |
|
57 | 76 | import static java.util.Collections.singleton;
|
58 | 77 |
|
59 | 78 | public class ValueCountAggregatorTests extends AggregatorTestCase {
|
60 | 79 |
|
61 | 80 | private static final String FIELD_NAME = "field";
|
62 | 81 |
|
| 82 | + private static final String STRING_VALUE_SCRIPT = "string_value"; |
| 83 | + private static final String NUMBER_VALUE_SCRIPT = "number_value"; |
| 84 | + private static final String SINGLE_SCRIPT = "single"; |
| 85 | + |
| 86 | + @Override |
| 87 | + protected AggregationBuilder createAggBuilderForTypeTest(MappedFieldType fieldType, String fieldName) { |
| 88 | + return new ValueCountAggregationBuilder("foo", null).field(fieldName); |
| 89 | + } |
| 90 | + |
| 91 | + @Override |
| 92 | + protected List<ValuesSourceType> getSupportedValuesSourceTypes() { |
| 93 | + return Arrays.asList( |
| 94 | + CoreValuesSourceType.NUMERIC, |
| 95 | + CoreValuesSourceType.BYTES, |
| 96 | + CoreValuesSourceType.GEOPOINT, |
| 97 | + CoreValuesSourceType.RANGE |
| 98 | + ); |
| 99 | + } |
| 100 | + |
| 101 | + @Override |
| 102 | + protected ScriptService getMockScriptService() { |
| 103 | + Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>(); |
| 104 | + |
| 105 | + scripts.put(STRING_VALUE_SCRIPT, vars -> (Double.valueOf((String) vars.get("_value")) + 1)); |
| 106 | + scripts.put(NUMBER_VALUE_SCRIPT, vars -> (((Number) vars.get("_value")).doubleValue() + 1)); |
| 107 | + scripts.put(SINGLE_SCRIPT, vars -> 1); |
| 108 | + |
| 109 | + MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME, |
| 110 | + scripts, |
| 111 | + Collections.emptyMap()); |
| 112 | + Map<String, ScriptEngine> engines = Collections.singletonMap(scriptEngine.getType(), scriptEngine); |
| 113 | + |
| 114 | + return new ScriptService(Settings.EMPTY, engines, ScriptModule.CORE_CONTEXTS); |
| 115 | + } |
| 116 | + |
| 117 | + |
| 118 | + public void testGeoField() throws IOException { |
| 119 | + testCase(new MatchAllDocsQuery(), ValueType.GEOPOINT, iw -> { |
| 120 | + for (int i = 0; i < 10; i++) { |
| 121 | + Document document = new Document(); |
| 122 | + document.add(new LatLonDocValuesField("field", 10, 10)); |
| 123 | + iw.addDocument(document); |
| 124 | + } |
| 125 | + }, count -> assertEquals(10L, count.getValue())); |
| 126 | + } |
| 127 | + |
| 128 | + public void testDoubleField() throws IOException { |
| 129 | + testCase(new MatchAllDocsQuery(), ValueType.DOUBLE, iw -> { |
| 130 | + for (int i = 0; i < 15; i++) { |
| 131 | + Document document = new Document(); |
| 132 | + document.add(new DoubleDocValuesField(FIELD_NAME, 23D)); |
| 133 | + iw.addDocument(document); |
| 134 | + } |
| 135 | + }, count -> assertEquals(15L, count.getValue())); |
| 136 | + } |
| 137 | + |
| 138 | + public void testKeyWordField() throws IOException { |
| 139 | + testCase(new MatchAllDocsQuery(), ValueType.STRING, iw -> { |
| 140 | + for (int i = 0; i < 20; i++) { |
| 141 | + Document document = new Document(); |
| 142 | + document.add(new SortedSetDocValuesField(FIELD_NAME, new BytesRef("stringValue"))); |
| 143 | + document.add(new SortedSetDocValuesField(FIELD_NAME, new BytesRef("string11Value"))); |
| 144 | + iw.addDocument(document); |
| 145 | + } |
| 146 | + }, count -> assertEquals(40L, count.getValue())); |
| 147 | + } |
| 148 | + |
63 | 149 | public void testNoDocs() throws IOException {
|
64 | 150 | for (ValueType valueType : ValueType.values()) {
|
65 | 151 | testCase(new MatchAllDocsQuery(), valueType, iw -> {
|
@@ -189,6 +275,105 @@ public void testRangeFieldValues() throws IOException {
|
189 | 275 | }, fieldType);
|
190 | 276 | }
|
191 | 277 |
|
| 278 | + public void testValueScriptNumber() throws IOException { |
| 279 | + ValueCountAggregationBuilder aggregationBuilder = new ValueCountAggregationBuilder("name", null) |
| 280 | + .field(FIELD_NAME) |
| 281 | + .script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, NUMBER_VALUE_SCRIPT, Collections.emptyMap())); |
| 282 | + |
| 283 | + MappedFieldType fieldType = createMappedFieldType(ValueType.NUMERIC); |
| 284 | + fieldType.setName(FIELD_NAME); |
| 285 | + fieldType.setHasDocValues(true); |
| 286 | + |
| 287 | + testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { |
| 288 | + iw.addDocument(singleton(new NumericDocValuesField(FIELD_NAME, 7))); |
| 289 | + iw.addDocument(singleton(new NumericDocValuesField(FIELD_NAME, 8))); |
| 290 | + iw.addDocument(singleton(new NumericDocValuesField(FIELD_NAME, 9))); |
| 291 | + }, card -> { |
| 292 | + assertEquals(3, card.getValue(), 0); |
| 293 | + assertTrue(AggregationInspectionHelper.hasValue(card)); |
| 294 | + }, fieldType); |
| 295 | + } |
| 296 | + |
| 297 | + public void testSingleScriptNumber() throws IOException { |
| 298 | + ValueCountAggregationBuilder aggregationBuilder = new ValueCountAggregationBuilder("name", null) |
| 299 | + .field(FIELD_NAME); |
| 300 | + |
| 301 | + MappedFieldType fieldType = createMappedFieldType(ValueType.NUMERIC); |
| 302 | + fieldType.setName(FIELD_NAME); |
| 303 | + fieldType.setHasDocValues(true); |
| 304 | + |
| 305 | + testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { |
| 306 | + Document doc = new Document(); |
| 307 | + doc.add(new SortedNumericDocValuesField(FIELD_NAME, 7)); |
| 308 | + doc.add(new SortedNumericDocValuesField(FIELD_NAME, 7)); |
| 309 | + iw.addDocument(doc); |
| 310 | + |
| 311 | + doc = new Document(); |
| 312 | + doc.add(new SortedNumericDocValuesField(FIELD_NAME, 8)); |
| 313 | + doc.add(new SortedNumericDocValuesField(FIELD_NAME, 8)); |
| 314 | + iw.addDocument(doc); |
| 315 | + |
| 316 | + doc = new Document(); |
| 317 | + doc.add(new SortedNumericDocValuesField(FIELD_NAME, 1)); |
| 318 | + doc.add(new SortedNumericDocValuesField(FIELD_NAME, 1)); |
| 319 | + iw.addDocument(doc); |
| 320 | + }, card -> { |
| 321 | + // note: this is 6, even though the script returns a single value. ValueCount does not de-dedupe |
| 322 | + assertEquals(6, card.getValue(), 0); |
| 323 | + assertTrue(AggregationInspectionHelper.hasValue(card)); |
| 324 | + }, fieldType); |
| 325 | + } |
| 326 | + |
| 327 | + public void testValueScriptString() throws IOException { |
| 328 | + ValueCountAggregationBuilder aggregationBuilder = new ValueCountAggregationBuilder("name", null) |
| 329 | + .field(FIELD_NAME) |
| 330 | + .script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, STRING_VALUE_SCRIPT, Collections.emptyMap())); |
| 331 | + |
| 332 | + MappedFieldType fieldType = createMappedFieldType(ValueType.STRING); |
| 333 | + fieldType.setName(FIELD_NAME); |
| 334 | + fieldType.setHasDocValues(true); |
| 335 | + |
| 336 | + testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { |
| 337 | + iw.addDocument(singleton(new SortedDocValuesField(FIELD_NAME, new BytesRef("1")))); |
| 338 | + iw.addDocument(singleton(new SortedDocValuesField(FIELD_NAME, new BytesRef("2")))); |
| 339 | + iw.addDocument(singleton(new SortedDocValuesField(FIELD_NAME, new BytesRef("3")))); |
| 340 | + }, card -> { |
| 341 | + assertEquals(3, card.getValue(), 0); |
| 342 | + assertTrue(AggregationInspectionHelper.hasValue(card)); |
| 343 | + }, fieldType); |
| 344 | + } |
| 345 | + |
| 346 | + public void testSingleScriptString() throws IOException { |
| 347 | + ValueCountAggregationBuilder aggregationBuilder = new ValueCountAggregationBuilder("name", null) |
| 348 | + .field(FIELD_NAME); |
| 349 | + |
| 350 | + MappedFieldType fieldType = createMappedFieldType(ValueType.STRING); |
| 351 | + fieldType.setName(FIELD_NAME); |
| 352 | + fieldType.setHasDocValues(true); |
| 353 | + |
| 354 | + testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { |
| 355 | + Document doc = new Document(); |
| 356 | + // Note: unlike numerics, lucene de-dupes strings so we increment here |
| 357 | + doc.add(new SortedSetDocValuesField(FIELD_NAME, new BytesRef("1"))); |
| 358 | + doc.add(new SortedSetDocValuesField(FIELD_NAME, new BytesRef("2"))); |
| 359 | + iw.addDocument(doc); |
| 360 | + |
| 361 | + doc = new Document(); |
| 362 | + doc.add(new SortedSetDocValuesField(FIELD_NAME, new BytesRef("3"))); |
| 363 | + doc.add(new SortedSetDocValuesField(FIELD_NAME, new BytesRef("4"))); |
| 364 | + iw.addDocument(doc); |
| 365 | + |
| 366 | + doc = new Document(); |
| 367 | + doc.add(new SortedSetDocValuesField(FIELD_NAME, new BytesRef("5"))); |
| 368 | + doc.add(new SortedSetDocValuesField(FIELD_NAME, new BytesRef("6"))); |
| 369 | + iw.addDocument(doc); |
| 370 | + }, card -> { |
| 371 | + // note: this is 6, even though the script returns a single value. ValueCount does not de-dedupe |
| 372 | + assertEquals(6, card.getValue(), 0); |
| 373 | + assertTrue(AggregationInspectionHelper.hasValue(card)); |
| 374 | + }, fieldType); |
| 375 | + } |
| 376 | + |
192 | 377 | private void testCase(Query query,
|
193 | 378 | ValueType valueType,
|
194 | 379 | CheckedConsumer<RandomIndexWriter, IOException> indexer,
|
|
0 commit comments