32
32
import org .apache .lucene .search .Query ;
33
33
import org .apache .lucene .store .Directory ;
34
34
import org .elasticsearch .common .CheckedConsumer ;
35
+ import org .elasticsearch .common .settings .Settings ;
35
36
import org .elasticsearch .index .mapper .MappedFieldType ;
36
37
import org .elasticsearch .index .mapper .NumberFieldMapper ;
38
+ import org .elasticsearch .script .MockScriptEngine ;
39
+ import org .elasticsearch .script .Script ;
40
+ import org .elasticsearch .script .ScriptEngine ;
41
+ import org .elasticsearch .script .ScriptModule ;
42
+ import org .elasticsearch .script .ScriptService ;
43
+ import org .elasticsearch .script .ScriptType ;
44
+ import org .elasticsearch .search .aggregations .AggregationBuilder ;
45
+ import org .elasticsearch .search .aggregations .Aggregator ;
37
46
import org .elasticsearch .search .aggregations .AggregatorTestCase ;
38
47
import org .elasticsearch .search .aggregations .support .AggregationInspectionHelper ;
48
+ import org .elasticsearch .search .aggregations .support .CoreValuesSourceType ;
49
+ import org .elasticsearch .search .aggregations .support .ValuesSourceType ;
39
50
import org .hamcrest .Description ;
40
51
import org .hamcrest .TypeSafeMatcher ;
41
52
42
53
import java .io .IOException ;
43
54
import java .util .ArrayList ;
44
55
import java .util .Arrays ;
56
+ import java .util .Collections ;
57
+ import java .util .HashMap ;
45
58
import java .util .List ;
59
+ import java .util .Map ;
46
60
import java .util .function .Consumer ;
47
61
import java .util .function .Function ;
48
62
import java .util .stream .IntStream ;
49
63
50
64
import static java .util .Collections .singleton ;
65
+ import static java .util .Collections .singletonList ;
51
66
import static org .elasticsearch .search .aggregations .metrics .MedianAbsoluteDeviationAggregatorTests .ExactMedianAbsoluteDeviation .calculateMAD ;
52
67
import static org .elasticsearch .search .aggregations .metrics .MedianAbsoluteDeviationAggregatorTests .IsCloseToRelative .closeToRelative ;
53
68
import static org .hamcrest .Matchers .equalTo ;
@@ -56,6 +71,11 @@ public class MedianAbsoluteDeviationAggregatorTests extends AggregatorTestCase {
56
71
57
72
private static final int SAMPLE_MIN = -1000000 ;
58
73
private static final int SAMPLE_MAX = 1000000 ;
74
+ public static final String FIELD_NAME = "number" ;
75
+
76
+ /** Script to return the {@code _value} provided by aggs framework. */
77
+ private static final String VALUE_SCRIPT = "_value" ;
78
+ private static final String SINGLE_SCRIPT = "single" ;
59
79
60
80
private static <T extends IndexableField > CheckedConsumer <RandomIndexWriter , IOException > randomSample (
61
81
int size ,
@@ -96,10 +116,10 @@ public void testSomeMatchesSortedNumericDocValues() throws IOException {
96
116
final int size = randomIntBetween (100 , 1000 );
97
117
final List <Long > sample = new ArrayList <>(size );
98
118
testCase (
99
- new DocValuesFieldExistsQuery ("number" ),
119
+ new DocValuesFieldExistsQuery (FIELD_NAME ),
100
120
randomSample (size , point -> {
101
121
sample .add (point );
102
- return singleton (new SortedNumericDocValuesField ("number" , point ));
122
+ return singleton (new SortedNumericDocValuesField (FIELD_NAME , point ));
103
123
}),
104
124
agg -> {
105
125
assertThat (agg .getMedianAbsoluteDeviation (), closeToRelative (calculateMAD (sample )));
@@ -112,10 +132,10 @@ public void testSomeMatchesNumericDocValues() throws IOException {
112
132
final int size = randomIntBetween (100 , 1000 );
113
133
final List <Long > sample = new ArrayList <>(size );
114
134
testCase (
115
- new DocValuesFieldExistsQuery ("number" ),
135
+ new DocValuesFieldExistsQuery (FIELD_NAME ),
116
136
randomSample (size , point -> {
117
137
sample .add (point );
118
- return singleton (new NumericDocValuesField ("number" , point ));
138
+ return singleton (new NumericDocValuesField (FIELD_NAME , point ));
119
139
}),
120
140
agg -> {
121
141
assertThat (agg .getMedianAbsoluteDeviation (), closeToRelative (calculateMAD (sample )));
@@ -130,10 +150,10 @@ public void testQueryFiltering() throws IOException {
130
150
final int [] sample = IntStream .rangeClosed (1 , 1000 ).toArray ();
131
151
final int [] filteredSample = Arrays .stream (sample ).filter (point -> point >= lowerRange && point <= upperRange ).toArray ();
132
152
testCase (
133
- IntPoint .newRangeQuery ("number" , lowerRange , upperRange ),
153
+ IntPoint .newRangeQuery (FIELD_NAME , lowerRange , upperRange ),
134
154
writer -> {
135
155
for (int point : sample ) {
136
- writer .addDocument (Arrays .asList (new IntPoint ("number" , point ), new SortedNumericDocValuesField ("number" , point )));
156
+ writer .addDocument (Arrays .asList (new IntPoint (FIELD_NAME , point ), new SortedNumericDocValuesField (FIELD_NAME , point )));
137
157
}
138
158
},
139
159
agg -> {
@@ -145,10 +165,10 @@ public void testQueryFiltering() throws IOException {
145
165
146
166
public void testQueryFiltersAll () throws IOException {
147
167
testCase (
148
- IntPoint .newRangeQuery ("number" , -1 , 0 ),
168
+ IntPoint .newRangeQuery (FIELD_NAME , -1 , 0 ),
149
169
writer -> {
150
- writer .addDocument (Arrays .asList (new IntPoint ("number" , 1 ), new SortedNumericDocValuesField ("number" , 1 )));
151
- writer .addDocument (Arrays .asList (new IntPoint ("number" , 2 ), new SortedNumericDocValuesField ("number" , 2 )));
170
+ writer .addDocument (Arrays .asList (new IntPoint (FIELD_NAME , 1 ), new SortedNumericDocValuesField (FIELD_NAME , 1 )));
171
+ writer .addDocument (Arrays .asList (new IntPoint (FIELD_NAME , 2 ), new SortedNumericDocValuesField (FIELD_NAME , 2 )));
152
172
},
153
173
agg -> {
154
174
assertThat (agg .getMedianAbsoluteDeviation (), equalTo (Double .NaN ));
@@ -157,34 +177,110 @@ public void testQueryFiltersAll() throws IOException {
157
177
);
158
178
}
159
179
180
+ public void testUnmapped () throws IOException {
181
+ MedianAbsoluteDeviationAggregationBuilder aggregationBuilder = new MedianAbsoluteDeviationAggregationBuilder ("foo" )
182
+ .field (FIELD_NAME );
183
+
184
+ testCase (aggregationBuilder , new DocValuesFieldExistsQuery (FIELD_NAME ), iw -> {
185
+ iw .addDocument (singleton (new NumericDocValuesField (FIELD_NAME , 7 )));
186
+ iw .addDocument (singleton (new NumericDocValuesField (FIELD_NAME , 1 )));
187
+ }, agg -> {
188
+ assertEquals (Double .NaN , agg .getMedianAbsoluteDeviation (),0 );
189
+ assertFalse (AggregationInspectionHelper .hasValue (agg ));
190
+ }, null );
191
+ }
192
+
193
+ public void testUnmappedMissing () throws IOException {
194
+ MedianAbsoluteDeviationAggregationBuilder aggregationBuilder = new MedianAbsoluteDeviationAggregationBuilder ("foo" )
195
+ .field (FIELD_NAME )
196
+ .missing (1234 );
197
+
198
+ testCase (aggregationBuilder , new MatchAllDocsQuery (), iw -> {
199
+ iw .addDocument (singleton (new NumericDocValuesField ("unrelatedField" , 7 )));
200
+ iw .addDocument (singleton (new NumericDocValuesField ("unrelatedField" , 8 )));
201
+ iw .addDocument (singleton (new NumericDocValuesField ("unrelatedField" , 9 )));
202
+ }, agg -> {
203
+ assertEquals (0 , agg .getMedianAbsoluteDeviation (), 0 );
204
+ assertTrue (AggregationInspectionHelper .hasValue (agg ));
205
+ }, null );
206
+ }
207
+
208
+ public void testValueScript () throws IOException {
209
+ MappedFieldType fieldType = new NumberFieldMapper .NumberFieldType (NumberFieldMapper .NumberType .LONG );
210
+ fieldType .setName (FIELD_NAME );
211
+ fieldType .setHasDocValues (true );
212
+
213
+ MedianAbsoluteDeviationAggregationBuilder aggregationBuilder = new MedianAbsoluteDeviationAggregationBuilder ("foo" )
214
+ .field (FIELD_NAME )
215
+ .script (new Script (ScriptType .INLINE , MockScriptEngine .NAME , VALUE_SCRIPT , Collections .emptyMap ()));
216
+
217
+ final int size = randomIntBetween (100 , 1000 );
218
+ final List <Long > sample = new ArrayList <>(size );
219
+ testCase (aggregationBuilder ,
220
+ new MatchAllDocsQuery (),
221
+ randomSample (size , point -> {
222
+ sample .add (point );
223
+ return singleton (new SortedNumericDocValuesField (FIELD_NAME , point ));
224
+ }),
225
+ agg -> {
226
+ assertThat (agg .getMedianAbsoluteDeviation (), closeToRelative (calculateMAD (sample )));
227
+ assertTrue (AggregationInspectionHelper .hasValue (agg ));
228
+ }, fieldType );
229
+ }
230
+
231
+ public void testSingleScript () throws IOException {
232
+ MedianAbsoluteDeviationAggregationBuilder aggregationBuilder = new MedianAbsoluteDeviationAggregationBuilder ("foo" )
233
+ .script (new Script (ScriptType .INLINE , MockScriptEngine .NAME , SINGLE_SCRIPT , Collections .emptyMap ()));
234
+
235
+ MappedFieldType fieldType = new NumberFieldMapper .NumberFieldType (NumberFieldMapper .NumberType .LONG );
236
+ fieldType .setName (FIELD_NAME );
237
+
238
+ final int size = randomIntBetween (100 , 1000 );
239
+ final List <Long > sample = new ArrayList <>(size );
240
+ testCase (aggregationBuilder ,
241
+ new MatchAllDocsQuery (),
242
+ iw -> {
243
+ for (int i = 0 ; i < 10 ; i ++) {
244
+ iw .addDocument (singleton (new NumericDocValuesField (FIELD_NAME , i + 1 )));
245
+ }
246
+ },
247
+ agg -> {
248
+ assertEquals (0 , agg .getMedianAbsoluteDeviation (), 0 );
249
+ assertTrue (AggregationInspectionHelper .hasValue (agg ));
250
+ }, fieldType );
251
+ }
252
+
160
253
private void testCase (Query query ,
161
- CheckedConsumer <RandomIndexWriter , IOException > buildIndex ,
254
+ CheckedConsumer <RandomIndexWriter ,
255
+ IOException > buildIndex ,
162
256
Consumer <InternalMedianAbsoluteDeviation > verify ) throws IOException {
257
+ MedianAbsoluteDeviationAggregationBuilder builder = new MedianAbsoluteDeviationAggregationBuilder ("mad" )
258
+ .field (FIELD_NAME )
259
+ .compression (randomDoubleBetween (20 , 1000 , true ));
260
+
261
+ MappedFieldType fieldType = new NumberFieldMapper .NumberFieldType (NumberFieldMapper .NumberType .LONG );
262
+ fieldType .setName (FIELD_NAME );
263
+
264
+ testCase (builder , query , buildIndex , verify , fieldType );
265
+ }
163
266
267
+ private void testCase (MedianAbsoluteDeviationAggregationBuilder aggregationBuilder , Query query ,
268
+ CheckedConsumer <RandomIndexWriter , IOException > indexer ,
269
+ Consumer <InternalMedianAbsoluteDeviation > verify , MappedFieldType fieldType ) throws IOException {
164
270
try (Directory directory = newDirectory ()) {
165
271
try (RandomIndexWriter indexWriter = new RandomIndexWriter (random (), directory )) {
166
- buildIndex .accept (indexWriter );
272
+ indexer .accept (indexWriter );
167
273
}
168
274
169
275
try (IndexReader indexReader = DirectoryReader .open (directory )) {
170
276
IndexSearcher indexSearcher = newSearcher (indexReader , true , true );
171
-
172
- MedianAbsoluteDeviationAggregationBuilder builder = new MedianAbsoluteDeviationAggregationBuilder ("mad" )
173
- .field ("number" )
174
- .compression (randomDoubleBetween (20 , 1000 , true ));
175
-
176
- MappedFieldType fieldType = new NumberFieldMapper .NumberFieldType (NumberFieldMapper .NumberType .LONG );
177
- fieldType .setName ("number" );
178
-
179
- MedianAbsoluteDeviationAggregator aggregator = createAggregator (builder , indexSearcher , fieldType );
277
+ Aggregator aggregator = createAggregator (aggregationBuilder , indexSearcher , fieldType );
180
278
aggregator .preCollection ();
181
279
indexSearcher .search (query , aggregator );
182
280
aggregator .postCollection ();
183
-
184
281
verify .accept ((InternalMedianAbsoluteDeviation ) aggregator .buildAggregation (0L ));
185
282
}
186
283
}
187
-
188
284
}
189
285
190
286
public static class IsCloseToRelative extends TypeSafeMatcher <Double > {
@@ -271,6 +367,30 @@ private static double calculateMedian(double[] sample) {
271
367
}
272
368
return median ;
273
369
}
370
+ }
371
+
372
+ @ Override
373
+ protected List <ValuesSourceType > getSupportedValuesSourceTypes () {
374
+ return singletonList (CoreValuesSourceType .NUMERIC );
375
+ }
376
+
377
+ @ Override
378
+ protected AggregationBuilder createAggBuilderForTypeTest (MappedFieldType fieldType , String fieldName ) {
379
+ return new MedianAbsoluteDeviationAggregationBuilder ("foo" ).field (fieldName );
380
+ }
381
+
382
+ @ Override
383
+ protected ScriptService getMockScriptService () {
384
+ Map <String , Function <Map <String , Object >, Object >> scripts = new HashMap <>();
385
+
386
+ scripts .put (VALUE_SCRIPT , vars -> ((Number ) vars .get ("_value" )).doubleValue () + 1 );
387
+ scripts .put (SINGLE_SCRIPT , vars -> 1 );
388
+
389
+ MockScriptEngine scriptEngine = new MockScriptEngine (MockScriptEngine .NAME ,
390
+ scripts ,
391
+ Collections .emptyMap ());
392
+ Map <String , ScriptEngine > engines = Collections .singletonMap (scriptEngine .getType (), scriptEngine );
274
393
394
+ return new ScriptService (Settings .EMPTY , engines , ScriptModule .CORE_CONTEXTS );
275
395
}
276
396
}
0 commit comments