Adds unit test for sampler aggregation (#23243)

* Adds unit test for sampler aggregation

Relates to #22278
This commit is contained in:
Nik Everett 2017-02-21 12:51:47 -05:00 committed by GitHub
commit 7475175957
2 changed files with 104 additions and 21 deletions

View File

@ -29,12 +29,13 @@ import org.apache.lucene.search.QueryCachingPolicy;
import org.apache.lucene.search.Weight;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.lease.Releasable;
import org.elasticsearch.common.lease.Releasables;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.cache.query.DisabledQueryCache;
import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.IndexFieldDataCache;
import org.elasticsearch.index.fielddata.IndexFieldDataService;
import org.elasticsearch.index.mapper.MappedFieldType;
@ -56,6 +57,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -65,6 +68,8 @@ import static org.mockito.Mockito.when;
* {@link AggregationBuilder} instance.
*/
public abstract class AggregatorTestCase extends ESTestCase {
private List<Releasable> releasables = new ArrayList<>();
protected <A extends Aggregator, B extends AggregationBuilder> A createAggregator(B aggregationBuilder,
IndexSearcher indexSearcher,
MappedFieldType... fieldTypes) throws IOException {
@ -99,6 +104,12 @@ public abstract class AggregatorTestCase extends ESTestCase {
when(searchContext.bigArrays()).thenReturn(new MockBigArrays(Settings.EMPTY, circuitBreakerService));
when(searchContext.fetchPhase())
.thenReturn(new FetchPhase(Arrays.asList(new FetchSourceSubPhase(), new DocValueFieldsFetchSubPhase())));
doAnswer(invocation -> {
/* Store the releasables so we can release them at the end of the test case. This is important because aggregations don't
* close their sub-aggregations. This is fairly similar to what the production code does. */
releasables.add((Releasable) invocation.getArguments()[0]);
return null;
}).when(searchContext).addReleasable(anyObject(), anyObject());
// TODO: now just needed for top_hits, this will need to be revised for other agg unit tests:
MapperService mapperService = mock(MapperService.class);
@ -110,10 +121,9 @@ public abstract class AggregatorTestCase extends ESTestCase {
QueryShardContext queryShardContext = mock(QueryShardContext.class);
for (MappedFieldType fieldType : fieldTypes) {
IndexFieldData<?> fieldData = fieldType.fielddataBuilder().build(indexSettings, fieldType,
new IndexFieldDataCache.None(), circuitBreakerService, mock(MapperService.class));
when(queryShardContext.fieldMapper(fieldType.name())).thenReturn(fieldType);
when(queryShardContext.getForField(fieldType)).thenReturn(fieldData);
when(queryShardContext.getForField(fieldType)).then(invocation -> fieldType.fielddataBuilder().build(
indexSettings, fieldType, new IndexFieldDataCache.None(), circuitBreakerService, mock(MapperService.class)));
when(searchContext.getQueryShardContext()).thenReturn(queryShardContext);
}
@ -126,13 +136,17 @@ public abstract class AggregatorTestCase extends ESTestCase {
Query query,
AggregationBuilder builder,
MappedFieldType... fieldTypes) throws IOException {
try (C a = createAggregator(builder, searcher, fieldTypes)) {
C a = createAggregator(builder, searcher, fieldTypes);
try {
a.preCollection();
searcher.search(query, a);
a.postCollection();
@SuppressWarnings("unchecked")
A internalAgg = (A) a.buildAggregation(0L);
return internalAgg;
} finally {
Releasables.close(releasables);
releasables.clear();
}
}
@ -168,14 +182,10 @@ public abstract class AggregatorTestCase extends ESTestCase {
try {
for (ShardSearcher subSearcher : subSearchers) {
C a = createAggregator(builder, subSearcher, fieldTypes);
try {
a.preCollection();
subSearcher.search(weight, a);
a.postCollection();
aggs.add(a.buildAggregation(0L));
} finally {
closeAgg(a);
}
a.preCollection();
subSearcher.search(weight, a);
a.postCollection();
aggs.add(a.buildAggregation(0L));
}
if (aggs.isEmpty()) {
return null;
@ -195,14 +205,8 @@ public abstract class AggregatorTestCase extends ESTestCase {
return internalAgg;
}
} finally {
closeAgg(root);
}
}
private void closeAgg(Aggregator agg) {
agg.close();
for (Aggregator sub : ((AggregatorBase) agg).subAggregators) {
closeAgg(sub);
Releasables.close(releasables);
releasables.clear();
}
}

View File

@ -0,0 +1,79 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.bucket.sampler;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.SortedNumericDocValuesField;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.elasticsearch.index.analysis.AnalyzerScope;
import org.elasticsearch.index.analysis.NamedAnalyzer;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.NumberFieldMapper;
import org.elasticsearch.index.mapper.TextFieldMapper.TextFieldType;
import org.elasticsearch.search.aggregations.AggregatorTestCase;
import org.elasticsearch.search.aggregations.metrics.min.Min;
import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder;
import java.io.IOException;
public class SamplerAggregatorTests extends AggregatorTestCase {
/**
* Uses the sampler aggregation to find the minimum value of a field out of the top 3 scoring documents in a search.
*/
public void testSampler() throws IOException {
TextFieldType textFieldType = new TextFieldType();
textFieldType.setIndexAnalyzer(new NamedAnalyzer("foo", AnalyzerScope.GLOBAL, new StandardAnalyzer()));
MappedFieldType numericFieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG);
numericFieldType.setName("int");
try (Directory dir = newDirectory();
RandomIndexWriter w = new RandomIndexWriter(random(), dir)) {
for (long value : new long[] {7, 3, -10, -6, 5, 50}) {
Document doc = new Document();
StringBuilder text = new StringBuilder();
for (int i = 0; i < value; i++) {
text.append("good ");
}
doc.add(new Field("text", text.toString(), textFieldType));
doc.add(new SortedNumericDocValuesField("int", value));
w.addDocument(doc);
}
SamplerAggregationBuilder aggBuilder = new SamplerAggregationBuilder("sampler")
.shardSize(3)
.subAggregation(new MinAggregationBuilder("min")
.field("int"));
try (IndexReader reader = w.getReader()) {
IndexSearcher searcher = new IndexSearcher(reader);
Sampler sampler = searchAndReduce(searcher, new TermQuery(new Term("text", "good")), aggBuilder, textFieldType,
numericFieldType);
Min min = sampler.getAggregations().get("min");
assertEquals(5.0, min.getValue(), 0);
}
}
}
}