Skip to content

Commit ef27430

Browse files
add tests to StringStatsAggregatorTests (#54033)
This adds tests for supported ValuesSourceTypes, unmapped fields, scripting, and the missing param. Also changes the supported type tests to catch AssertionError, which is what string stats produces on some unsupported field types. And when this test fails it now lists the original exception/assertion error as a cause
1 parent f38b7d7 commit ef27430

File tree

3 files changed

+246
-4
lines changed

3 files changed

+246
-4
lines changed

test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -683,10 +683,11 @@ public final void testSupportedFieldTypes() throws IOException {
683683
fail("Aggregator [" + aggregationBuilder.getType() + "] should not support field type ["
684684
+ fieldType.typeName() + "] but executing against the field did not throw an exception");
685685
}
686-
} catch (Exception e) {
686+
} catch (Exception | AssertionError e) {
687687
if (supportedVSTypes.contains(vst) && unsupportedMappedFieldTypes.contains(fieldType.typeName()) == false) {
688-
fail("Aggregator [" + aggregationBuilder.getType() + "] supports field type ["
689-
+ fieldType.typeName() + "] but executing against the field threw an exception: [" + e.getMessage() + "]");
688+
throw new AssertionError("Aggregator [" + aggregationBuilder.getType() + "] supports field type ["
689+
+ fieldType.typeName() + "] but executing against the field threw an exception: [" + e.getMessage() + "]",
690+
e);
690691
}
691692
}
692693
}
@@ -706,7 +707,7 @@ private void writeTestDoc(MappedFieldType fieldType, String fieldName, RandomInd
706707
ValuesSourceType vst = fieldType.getValuesSourceType();
707708
Document doc = new Document();
708709
String json;
709-
710+
710711
if (vst.equals(CoreValuesSourceType.NUMERIC)) {
711712
// TODO note: once VS refactor adds DATE/BOOLEAN, this conditional will go away
712713
long v;

x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/stringstats/StringStatsAggregatorTests.java

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,46 @@
1818
import org.apache.lucene.store.Directory;
1919
import org.apache.lucene.util.BytesRef;
2020
import org.elasticsearch.common.CheckedConsumer;
21+
import org.elasticsearch.common.settings.Settings;
22+
import org.elasticsearch.index.mapper.IpFieldMapper;
2123
import org.elasticsearch.index.mapper.MappedFieldType;
2224
import org.elasticsearch.index.mapper.NumberFieldMapper;
2325
import org.elasticsearch.index.mapper.TextFieldMapper;
26+
import org.elasticsearch.script.MockScriptEngine;
27+
import org.elasticsearch.script.Script;
28+
import org.elasticsearch.script.ScriptEngine;
29+
import org.elasticsearch.script.ScriptModule;
30+
import org.elasticsearch.script.ScriptService;
31+
import org.elasticsearch.script.ScriptType;
2432
import org.elasticsearch.search.aggregations.AggregationBuilder;
2533
import org.elasticsearch.search.aggregations.AggregatorTestCase;
2634
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
2735
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
2836
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregator;
37+
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
2938
import org.elasticsearch.search.aggregations.support.ValueType;
39+
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
40+
import org.elasticsearch.search.lookup.LeafDocLookup;
3041

3142
import java.io.IOException;
3243
import java.util.List;
44+
import java.util.Map;
45+
import java.util.Set;
3346
import java.util.function.Consumer;
47+
import java.util.function.Function;
3448

49+
import static java.util.Collections.emptyMap;
50+
import static java.util.Collections.emptySet;
3551
import static java.util.Collections.singleton;
52+
import static java.util.Collections.singletonList;
53+
import static java.util.Collections.singletonMap;
54+
import static java.util.stream.Collectors.toList;
3655

3756
public class StringStatsAggregatorTests extends AggregatorTestCase {
3857

58+
private static final String VALUE_SCRIPT_NAME = "value_script";
59+
private static final String FIELD_SCRIPT_NAME = "field_script";
60+
3961
private void testCase(Query query,
4062
CheckedConsumer<RandomIndexWriter, IOException> buildIndex,
4163
Consumer<InternalStringStats> verify) throws IOException {
@@ -119,6 +141,32 @@ public void testUnmappedWithMissingField() throws IOException {
119141
}, null);
120142
}
121143

144+
public void testMissing() throws IOException {
145+
final TextFieldMapper.TextFieldType fieldType = new TextFieldMapper.TextFieldType();
146+
fieldType.setName("text");
147+
fieldType.setFielddata(true);
148+
149+
final StringStatsAggregationBuilder aggregationBuilder = new StringStatsAggregationBuilder("_name")
150+
.field(fieldType.name())
151+
.missing("b");
152+
153+
testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> {
154+
iw.addDocument(singleton(new TextField(fieldType.name(), "a", Field.Store.NO)));
155+
iw.addDocument(emptySet());
156+
iw.addDocument(singleton(new TextField(fieldType.name(), "a", Field.Store.NO)));
157+
iw.addDocument(emptySet());
158+
}, stats -> {
159+
assertEquals(4, stats.getCount());
160+
assertEquals(1, stats.getMaxLength());
161+
assertEquals(1, stats.getMinLength());
162+
assertEquals(1.0, stats.getAvgLength(), 0);
163+
assertEquals(2, stats.getDistribution().size());
164+
assertEquals(0.5, stats.getDistribution().get("a"), 0);
165+
assertEquals(0.5, stats.getDistribution().get("b"), 0);
166+
assertEquals(1.0, stats.getEntropy(), 0);
167+
}, fieldType);
168+
}
169+
122170
public void testSingleValuedField() throws IOException {
123171
testCase(new MatchAllDocsQuery(), iw -> {
124172
for(int i=0; i < 10; i++) {
@@ -258,4 +306,144 @@ public void testNestedAggregation() throws IOException {
258306
directory.close();
259307
}
260308

309+
public void testValueScriptSingleValuedField() throws IOException {
310+
final TextFieldMapper.TextFieldType fieldType = new TextFieldMapper.TextFieldType();
311+
fieldType.setName("text");
312+
fieldType.setFielddata(true);
313+
314+
final StringStatsAggregationBuilder aggregationBuilder = new StringStatsAggregationBuilder("_name")
315+
.field(fieldType.name())
316+
.script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, VALUE_SCRIPT_NAME, emptyMap()));
317+
318+
testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> {
319+
iw.addDocument(singleton(new TextField(fieldType.name(), "b", Field.Store.NO)));
320+
iw.addDocument(singleton(new TextField(fieldType.name(), "b", Field.Store.NO)));
321+
}, stats -> {
322+
assertEquals(2, stats.getCount());
323+
assertEquals(2, stats.getMaxLength());
324+
assertEquals(2, stats.getMinLength());
325+
assertEquals(2.0, stats.getAvgLength(), 0);
326+
assertEquals(2, stats.getDistribution().size());
327+
assertEquals(0.5, stats.getDistribution().get("a"), 0);
328+
assertEquals(0.5, stats.getDistribution().get("b"), 0);
329+
assertEquals(1.0, stats.getEntropy(), 0);
330+
}, fieldType);
331+
}
332+
333+
public void testValueScriptMultiValuedField() throws IOException {
334+
final TextFieldMapper.TextFieldType fieldType = new TextFieldMapper.TextFieldType();
335+
fieldType.setName("text");
336+
fieldType.setFielddata(true);
337+
338+
final StringStatsAggregationBuilder aggregationBuilder = new StringStatsAggregationBuilder("_name")
339+
.field(fieldType.name())
340+
.script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, VALUE_SCRIPT_NAME, emptyMap()));
341+
342+
testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> {
343+
iw.addDocument(Set.of(
344+
new TextField(fieldType.name(), "b", Field.Store.NO),
345+
new TextField(fieldType.name(), "c", Field.Store.NO)
346+
));
347+
iw.addDocument(Set.of(
348+
new TextField(fieldType.name(), "b", Field.Store.NO),
349+
new TextField(fieldType.name(), "c", Field.Store.NO)
350+
));
351+
}, stats -> {
352+
assertEquals(4, stats.getCount());
353+
assertEquals(2, stats.getMaxLength());
354+
assertEquals(2, stats.getMinLength());
355+
assertEquals(2.0, stats.getAvgLength(), 0);
356+
assertEquals(3, stats.getDistribution().size());
357+
assertEquals(0.5, stats.getDistribution().get("a"), 0);
358+
assertEquals(0.25, stats.getDistribution().get("b"), 0);
359+
assertEquals(0.25, stats.getDistribution().get("c"), 0);
360+
assertEquals(1.5, stats.getEntropy(), 0);
361+
}, fieldType);
362+
}
363+
364+
public void testFieldScriptSingleValuedField() throws IOException {
365+
final TextFieldMapper.TextFieldType fieldType = new TextFieldMapper.TextFieldType();
366+
fieldType.setName("text");
367+
fieldType.setFielddata(true);
368+
369+
final StringStatsAggregationBuilder aggregationBuilder = new StringStatsAggregationBuilder("_name")
370+
.script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, FIELD_SCRIPT_NAME, singletonMap("field", fieldType.name())));
371+
372+
testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> {
373+
iw.addDocument(singleton(new TextField(fieldType.name(), "b", Field.Store.NO)));
374+
iw.addDocument(singleton(new TextField(fieldType.name(), "b", Field.Store.NO)));
375+
}, stats -> {
376+
assertEquals(2, stats.getCount());
377+
assertEquals(2, stats.getMaxLength());
378+
assertEquals(2, stats.getMinLength());
379+
assertEquals(2.0, stats.getAvgLength(), 0);
380+
assertEquals(2, stats.getDistribution().size());
381+
assertEquals(0.5, stats.getDistribution().get("a"), 0);
382+
assertEquals(0.5, stats.getDistribution().get("b"), 0);
383+
assertEquals(1.0, stats.getEntropy(), 0);
384+
}, fieldType);
385+
}
386+
387+
public void testFieldScriptMultiValuedField() throws IOException {
388+
final TextFieldMapper.TextFieldType fieldType = new TextFieldMapper.TextFieldType();
389+
fieldType.setName("text");
390+
fieldType.setFielddata(true);
391+
392+
final StringStatsAggregationBuilder aggregationBuilder = new StringStatsAggregationBuilder("_name")
393+
.script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, FIELD_SCRIPT_NAME, singletonMap("field", fieldType.name())));
394+
395+
testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> {
396+
iw.addDocument(Set.of(
397+
new TextField(fieldType.name(), "b", Field.Store.NO),
398+
new TextField(fieldType.name(), "c", Field.Store.NO)
399+
));
400+
iw.addDocument(Set.of(
401+
new TextField(fieldType.name(), "b", Field.Store.NO),
402+
new TextField(fieldType.name(), "c", Field.Store.NO)
403+
));
404+
}, stats -> {
405+
assertEquals(4, stats.getCount());
406+
assertEquals(2, stats.getMaxLength());
407+
assertEquals(2, stats.getMinLength());
408+
assertEquals(2.0, stats.getAvgLength(), 0);
409+
assertEquals(3, stats.getDistribution().size());
410+
assertEquals(0.5, stats.getDistribution().get("a"), 0);
411+
assertEquals(0.25, stats.getDistribution().get("b"), 0);
412+
assertEquals(0.25, stats.getDistribution().get("c"), 0);
413+
assertEquals(1.5, stats.getEntropy(), 0);
414+
}, fieldType);
415+
}
416+
417+
@Override
418+
protected AggregationBuilder createAggBuilderForTypeTest(MappedFieldType fieldType, String fieldName) {
419+
return new StringStatsAggregationBuilder("_name")
420+
.field(fieldName);
421+
}
422+
423+
@Override
424+
protected List<ValuesSourceType> getSupportedValuesSourceTypes() {
425+
return singletonList(CoreValuesSourceType.BYTES);
426+
}
427+
428+
@Override
429+
protected List<String> unsupportedMappedFieldTypes() {
430+
return singletonList(IpFieldMapper.CONTENT_TYPE);
431+
}
432+
433+
@Override
434+
protected ScriptService getMockScriptService() {
435+
final Map<String, Function<Map<String, Object>, Object>> scripts = Map.of(
436+
VALUE_SCRIPT_NAME, vars -> "a" + vars.get("_value"),
437+
FIELD_SCRIPT_NAME, vars -> {
438+
final String fieldName = (String) vars.get("field");
439+
final LeafDocLookup lookup = (LeafDocLookup) vars.get("doc");
440+
return lookup.get(fieldName).stream()
441+
.map(value -> "a" + value)
442+
.collect(toList());
443+
}
444+
);
445+
final MockScriptEngine engine = new MockScriptEngine(MockScriptEngine.NAME, scripts, emptyMap());
446+
final Map<String, ScriptEngine> engines = singletonMap(engine.getType(), engine);
447+
return new ScriptService(Settings.EMPTY, engines, ScriptModule.CORE_CONTEXTS);
448+
}
261449
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
setup:
2+
- do:
3+
indices.create:
4+
index: test
5+
body:
6+
mappings:
7+
properties:
8+
value:
9+
type: keyword
10+
- do:
11+
bulk:
12+
index: test
13+
refresh: true
14+
body:
15+
- '{"index": {}}'
16+
- '{"value": "a"}'
17+
- '{"index": {}}'
18+
- '{"value": "b"}'
19+
20+
---
21+
"basic":
22+
- do:
23+
search:
24+
size: 0
25+
body:
26+
aggs:
27+
my_agg:
28+
string_stats:
29+
field: value
30+
- match: { aggregations.my_agg.count: 2 }
31+
- match: { aggregations.my_agg.min_length: 1 }
32+
- match: { aggregations.my_agg.max_length: 1 }
33+
- match: { aggregations.my_agg.avg_length: 1.0 }
34+
- match: { aggregations.my_agg.entropy: 1.0 }
35+
36+
---
37+
"show_distribution":
38+
- do:
39+
search:
40+
size: 0
41+
body:
42+
aggs:
43+
my_agg:
44+
string_stats:
45+
field: value
46+
show_distribution: true
47+
- match: { aggregations.my_agg.count: 2 }
48+
- match: { aggregations.my_agg.min_length: 1 }
49+
- match: { aggregations.my_agg.max_length: 1 }
50+
- match: { aggregations.my_agg.avg_length: 1.0 }
51+
- match: { aggregations.my_agg.entropy: 1.0 }
52+
- match: { aggregations.my_agg.distribution.a: 0.5 }
53+
- match: { aggregations.my_agg.distribution.b: 0.5 }

0 commit comments

Comments
 (0)