Skip to content

Commit d73d89c

Browse files
nik9000cbuescher
authored andcommitted
Adds unit test for sampler aggregation
Relates to #22278
1 parent de339e1 commit d73d89c

File tree

2 files changed

+104
-21
lines changed

2 files changed

+104
-21
lines changed

core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@
2929
import org.apache.lucene.search.Weight;
3030
import org.elasticsearch.Version;
3131
import org.elasticsearch.cluster.metadata.IndexMetaData;
32+
import org.elasticsearch.common.lease.Releasable;
33+
import org.elasticsearch.common.lease.Releasables;
3234
import org.elasticsearch.common.settings.Settings;
3335
import org.elasticsearch.common.util.MockBigArrays;
3436
import org.elasticsearch.index.IndexSettings;
3537
import org.elasticsearch.index.cache.query.DisabledQueryCache;
3638
import org.elasticsearch.index.engine.Engine;
37-
import org.elasticsearch.index.fielddata.IndexFieldData;
3839
import org.elasticsearch.index.fielddata.IndexFieldDataCache;
3940
import org.elasticsearch.index.fielddata.IndexFieldDataService;
4041
import org.elasticsearch.index.mapper.MappedFieldType;
@@ -56,6 +57,8 @@
5657
import java.util.Collections;
5758
import java.util.List;
5859

60+
import static org.mockito.Matchers.anyObject;
61+
import static org.mockito.Mockito.doAnswer;
5962
import static org.mockito.Mockito.mock;
6063
import static org.mockito.Mockito.when;
6164

@@ -65,6 +68,8 @@
6568
* {@link AggregationBuilder} instance.
6669
*/
6770
public abstract class AggregatorTestCase extends ESTestCase {
71+
private List<Releasable> releasables = new ArrayList<>();
72+
6873
protected <A extends Aggregator, B extends AggregationBuilder> A createAggregator(B aggregationBuilder,
6974
IndexSearcher indexSearcher,
7075
MappedFieldType... fieldTypes) throws IOException {
@@ -99,6 +104,12 @@ public boolean shouldCache(Query query) throws IOException {
99104
when(searchContext.bigArrays()).thenReturn(new MockBigArrays(Settings.EMPTY, circuitBreakerService));
100105
when(searchContext.fetchPhase())
101106
.thenReturn(new FetchPhase(Arrays.asList(new FetchSourceSubPhase(), new DocValueFieldsFetchSubPhase())));
107+
doAnswer(invocation -> {
108+
/* Store the releasables so we can release them at the end of the test case. This is important because aggregations don't
109+
* close their sub-aggregations. This is fairly similar to what the production code does. */
110+
releasables.add((Releasable) invocation.getArguments()[0]);
111+
return null;
112+
}).when(searchContext).addReleasable(anyObject(), anyObject());
102113

103114
// TODO: now just needed for top_hits, this will need to be revised for other agg unit tests:
104115
MapperService mapperService = mock(MapperService.class);
@@ -110,10 +121,9 @@ public boolean shouldCache(Query query) throws IOException {
110121

111122
QueryShardContext queryShardContext = mock(QueryShardContext.class);
112123
for (MappedFieldType fieldType : fieldTypes) {
113-
IndexFieldData<?> fieldData = fieldType.fielddataBuilder().build(indexSettings, fieldType,
114-
new IndexFieldDataCache.None(), circuitBreakerService, mock(MapperService.class));
115124
when(queryShardContext.fieldMapper(fieldType.name())).thenReturn(fieldType);
116-
when(queryShardContext.getForField(fieldType)).thenReturn(fieldData);
125+
when(queryShardContext.getForField(fieldType)).then(invocation -> fieldType.fielddataBuilder().build(
126+
indexSettings, fieldType, new IndexFieldDataCache.None(), circuitBreakerService, mock(MapperService.class)));
117127
when(searchContext.getQueryShardContext()).thenReturn(queryShardContext);
118128
}
119129

@@ -126,13 +136,17 @@ protected <A extends InternalAggregation, C extends Aggregator> A search(IndexSe
126136
Query query,
127137
AggregationBuilder builder,
128138
MappedFieldType... fieldTypes) throws IOException {
129-
try (C a = createAggregator(builder, searcher, fieldTypes)) {
139+
C a = createAggregator(builder, searcher, fieldTypes);
140+
try {
130141
a.preCollection();
131142
searcher.search(query, a);
132143
a.postCollection();
133144
@SuppressWarnings("unchecked")
134145
A internalAgg = (A) a.buildAggregation(0L);
135146
return internalAgg;
147+
} finally {
148+
Releasables.close(releasables);
149+
releasables.clear();
136150
}
137151
}
138152

@@ -168,14 +182,10 @@ protected <A extends InternalAggregation, C extends Aggregator> A searchAndReduc
168182
try {
169183
for (ShardSearcher subSearcher : subSearchers) {
170184
C a = createAggregator(builder, subSearcher, fieldTypes);
171-
try {
172-
a.preCollection();
173-
subSearcher.search(weight, a);
174-
a.postCollection();
175-
aggs.add(a.buildAggregation(0L));
176-
} finally {
177-
closeAgg(a);
178-
}
185+
a.preCollection();
186+
subSearcher.search(weight, a);
187+
a.postCollection();
188+
aggs.add(a.buildAggregation(0L));
179189
}
180190
if (aggs.isEmpty()) {
181191
return null;
@@ -195,14 +205,8 @@ protected <A extends InternalAggregation, C extends Aggregator> A searchAndReduc
195205
return internalAgg;
196206
}
197207
} finally {
198-
closeAgg(root);
199-
}
200-
}
201-
202-
private void closeAgg(Aggregator agg) {
203-
agg.close();
204-
for (Aggregator sub : ((AggregatorBase) agg).subAggregators) {
205-
closeAgg(sub);
208+
Releasables.close(releasables);
209+
releasables.clear();
206210
}
207211
}
208212

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.search.aggregations.bucket.sampler;
21+
22+
import org.apache.lucene.analysis.standard.StandardAnalyzer;
23+
import org.apache.lucene.document.Document;
24+
import org.apache.lucene.document.Field;
25+
import org.apache.lucene.document.SortedNumericDocValuesField;
26+
import org.apache.lucene.index.IndexReader;
27+
import org.apache.lucene.index.RandomIndexWriter;
28+
import org.apache.lucene.index.Term;
29+
import org.apache.lucene.search.IndexSearcher;
30+
import org.apache.lucene.search.TermQuery;
31+
import org.apache.lucene.store.Directory;
32+
import org.elasticsearch.index.analysis.AnalyzerScope;
33+
import org.elasticsearch.index.analysis.NamedAnalyzer;
34+
import org.elasticsearch.index.mapper.MappedFieldType;
35+
import org.elasticsearch.index.mapper.NumberFieldMapper;
36+
import org.elasticsearch.index.mapper.TextFieldMapper.TextFieldType;
37+
import org.elasticsearch.search.aggregations.AggregatorTestCase;
38+
import org.elasticsearch.search.aggregations.metrics.min.Min;
39+
import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder;
40+
41+
import java.io.IOException;
42+
43+
public class SamplerAggregatorTests extends AggregatorTestCase {
44+
/**
45+
* Uses the sampler aggregation to find the minimum value of a field out of the top 3 scoring documents in a search.
46+
*/
47+
public void testSampler() throws IOException {
48+
TextFieldType textFieldType = new TextFieldType();
49+
textFieldType.setIndexAnalyzer(new NamedAnalyzer("foo", AnalyzerScope.GLOBAL, new StandardAnalyzer()));
50+
MappedFieldType numericFieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG);
51+
numericFieldType.setName("int");
52+
53+
try (Directory dir = newDirectory();
54+
RandomIndexWriter w = new RandomIndexWriter(random(), dir)) {
55+
for (long value : new long[] {7, 3, -10, -6, 5, 50}) {
56+
Document doc = new Document();
57+
StringBuilder text = new StringBuilder();
58+
for (int i = 0; i < value; i++) {
59+
text.append("good ");
60+
}
61+
doc.add(new Field("text", text.toString(), textFieldType));
62+
doc.add(new SortedNumericDocValuesField("int", value));
63+
w.addDocument(doc);
64+
}
65+
66+
SamplerAggregationBuilder aggBuilder = new SamplerAggregationBuilder("sampler")
67+
.shardSize(3)
68+
.subAggregation(new MinAggregationBuilder("min")
69+
.field("int"));
70+
try (IndexReader reader = w.getReader()) {
71+
IndexSearcher searcher = new IndexSearcher(reader);
72+
Sampler sampler = searchAndReduce(searcher, new TermQuery(new Term("text", "good")), aggBuilder, textFieldType,
73+
numericFieldType);
74+
Min min = sampler.getAggregations().get("min");
75+
assertEquals(5.0, min.getValue(), 0);
76+
}
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)