diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java new file mode 100644 index 00000000000..0d11ef5449c --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java @@ -0,0 +1,117 @@ +/* + * 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; + +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryCache; +import org.apache.lucene.search.QueryCachingPolicy; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.ParseFieldMatcher; +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.IndexFieldDataService; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.indices.breaker.CircuitBreakerService; +import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; +import org.elasticsearch.search.fetch.FetchPhase; +import org.elasticsearch.search.fetch.subphase.DocValueFieldsFetchSubPhase; +import org.elasticsearch.search.fetch.subphase.FetchSourceSubPhase; +import org.elasticsearch.search.internal.ContextIndexSearcher; +import org.elasticsearch.search.internal.SearchContext; +import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.Arrays; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Base class for testing {@link Aggregator} implementations. + * Provides a helper constructing the {@link Aggregator} implementation based on a provided {@link AggregationBuilder} instance. + */ +public abstract class AggregatorTestCase extends ESTestCase { + + protected A createAggregator(B aggregationBuilder, + MappedFieldType fieldType, + IndexSearcher indexSearcher) throws IOException { + IndexSettings indexSettings = new IndexSettings( + IndexMetaData.builder("_index").settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)) + .numberOfShards(1) + .numberOfReplicas(0) + .creationDate(System.currentTimeMillis()) + .build(), + Settings.EMPTY + ); + + Engine.Searcher searcher = new Engine.Searcher("aggregator_test", indexSearcher); + QueryCache queryCache = new DisabledQueryCache(indexSettings); + QueryCachingPolicy queryCachingPolicy = new QueryCachingPolicy() { + @Override + public void onUse(Query query) { + } + + @Override + public boolean shouldCache(Query query) throws IOException { + // never cache a query + return false; + } + }; + ContextIndexSearcher contextIndexSearcher = new ContextIndexSearcher(searcher, queryCache, queryCachingPolicy); + + CircuitBreakerService circuitBreakerService = new NoneCircuitBreakerService(); + SearchContext searchContext = mock(SearchContext.class); + when(searchContext.searcher()).thenReturn(contextIndexSearcher); + when(searchContext.parseFieldMatcher()).thenReturn(ParseFieldMatcher.STRICT); + when(searchContext.bigArrays()).thenReturn(new MockBigArrays(Settings.EMPTY, circuitBreakerService)); + when(searchContext.fetchPhase()) + .thenReturn(new FetchPhase(Arrays.asList(new FetchSourceSubPhase(), new DocValueFieldsFetchSubPhase()))); + + // TODO: now just needed for top_hits, this will need to be revised for other agg unit tests: + MapperService mapperService = mock(MapperService.class); + when(mapperService.hasNested()).thenReturn(false); + when(searchContext.mapperService()).thenReturn(mapperService); + + SearchLookup searchLookup = new SearchLookup(mapperService, mock(IndexFieldDataService.class), new String[]{"type"}); + when(searchContext.lookup()).thenReturn(searchLookup); + + QueryShardContext queryShardContext = mock(QueryShardContext.class); + IndexFieldData fieldData = fieldType.fielddataBuilder().build(indexSettings, fieldType, null, circuitBreakerService, + mock(MapperService.class)); + when(queryShardContext.fieldMapper(anyString())).thenReturn(fieldType); + when(queryShardContext.getForField(any())).thenReturn(fieldData); + when(searchContext.getQueryShardContext()).thenReturn(queryShardContext); + + @SuppressWarnings("unchecked") + A aggregator = (A) aggregationBuilder.build(searchContext, null).create(null, true); + return aggregator; + } + +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java new file mode 100644 index 00000000000..60613e26d72 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java @@ -0,0 +1,85 @@ +/* + * 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.terms; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.SortedSetDocValuesField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.mapper.KeywordFieldMapper; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.search.aggregations.AggregatorTestCase; +import org.elasticsearch.search.aggregations.support.ValueType; + +public class TermsAggregatorTests extends AggregatorTestCase { + + public void testTermsAggregator() throws Exception { + Directory directory = newDirectory(); + RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); + Document document = new Document(); + document.add(new SortedSetDocValuesField("string", new BytesRef("a"))); + document.add(new SortedSetDocValuesField("string", new BytesRef("b"))); + indexWriter.addDocument(document); + document = new Document(); + document.add(new SortedSetDocValuesField("string", new BytesRef("c"))); + document.add(new SortedSetDocValuesField("string", new BytesRef("a"))); + indexWriter.addDocument(document); + document = new Document(); + document.add(new SortedSetDocValuesField("string", new BytesRef("b"))); + document.add(new SortedSetDocValuesField("string", new BytesRef("d"))); + indexWriter.addDocument(document); + indexWriter.close(); + + IndexReader indexReader = DirectoryReader.open(directory); + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + + for (TermsAggregatorFactory.ExecutionMode executionMode : TermsAggregatorFactory.ExecutionMode.values()) { + TermsAggregationBuilder aggregationBuilder = new TermsAggregationBuilder("_name", ValueType.STRING) + .executionHint(executionMode.toString()) + .field("string") + .order(Terms.Order.term(true)); + MappedFieldType fieldType = new KeywordFieldMapper.KeywordFieldType(); + fieldType.setName("string"); + fieldType.setHasDocValues(true ); + try (TermsAggregator aggregator = createAggregator(aggregationBuilder, fieldType, indexSearcher)) { + aggregator.preCollection(); + indexSearcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + Terms result = (Terms) aggregator.buildAggregation(0L); + assertEquals(4, result.getBuckets().size()); + assertEquals("a", result.getBuckets().get(0).getKeyAsString()); + assertEquals(2L, result.getBuckets().get(0).getDocCount()); + assertEquals("b", result.getBuckets().get(1).getKeyAsString()); + assertEquals(2L, result.getBuckets().get(1).getDocCount()); + assertEquals("c", result.getBuckets().get(2).getKeyAsString()); + assertEquals(1L, result.getBuckets().get(2).getDocCount()); + assertEquals("d", result.getBuckets().get(3).getKeyAsString()); + assertEquals(1L, result.getBuckets().get(3).getDocCount()); + } + } + indexReader.close(); + directory.close(); + } + +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/MinAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/MinAggregatorTests.java new file mode 100644 index 00000000000..b9ee39f7275 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/MinAggregatorTests.java @@ -0,0 +1,163 @@ +/* + * 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.metrics.min; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.NumericDocValuesField; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.store.Directory; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.NumberFieldMapper; +import org.elasticsearch.search.aggregations.AggregatorTestCase; + +public class MinAggregatorTests extends AggregatorTestCase { + + public void testMinAggregator_numericDv() throws Exception { + Directory directory = newDirectory(); + RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); + Document document = new Document(); + document.add(new NumericDocValuesField("number", 9)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new NumericDocValuesField("number", 7)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new NumericDocValuesField("number", 5)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new NumericDocValuesField("number", 3)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new NumericDocValuesField("number", 1)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new NumericDocValuesField("number", -1)); + indexWriter.addDocument(document); + indexWriter.close(); + + IndexReader indexReader = DirectoryReader.open(directory); + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + + MinAggregationBuilder aggregationBuilder = new MinAggregationBuilder("_name").field("number"); + MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG); + fieldType.setName("number"); + try (MinAggregator aggregator = createAggregator(aggregationBuilder, fieldType, indexSearcher)) { + aggregator.preCollection(); + indexSearcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + InternalMin result = (InternalMin) aggregator.buildAggregation(0L); + assertEquals(-1.0, result.getValue(), 0); + } + indexReader.close(); + directory.close(); + } + + public void testMinAggregator_sortedNumericDv() throws Exception { + Directory directory = newDirectory(); + RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); + Document document = new Document(); + document.add(new SortedNumericDocValuesField("number", 9)); + document.add(new SortedNumericDocValuesField("number", 7)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new SortedNumericDocValuesField("number", 5)); + document.add(new SortedNumericDocValuesField("number", 3)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new SortedNumericDocValuesField("number", 1)); + document.add(new SortedNumericDocValuesField("number", -1)); + indexWriter.addDocument(document); + indexWriter.close(); + + IndexReader indexReader = DirectoryReader.open(directory); + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + + MinAggregationBuilder aggregationBuilder = new MinAggregationBuilder("_name").field("number"); + MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG); + fieldType.setName("number"); + try (MinAggregator aggregator = createAggregator(aggregationBuilder, fieldType, indexSearcher)) { + aggregator.preCollection(); + indexSearcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + InternalMin result = (InternalMin) aggregator.buildAggregation(0L); + assertEquals(-1.0, result.getValue(), 0); + } + indexReader.close(); + directory.close(); + } + + public void testMinAggregator_noValue() throws Exception { + Directory directory = newDirectory(); + RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); + Document document = new Document(); + document.add(new SortedNumericDocValuesField("number1", 7)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new SortedNumericDocValuesField("number1", 3)); + indexWriter.addDocument(document); + document = new Document(); + document.add(new SortedNumericDocValuesField("number1", 1)); + indexWriter.addDocument(document); + indexWriter.close(); + + IndexReader indexReader = DirectoryReader.open(directory); + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + + MinAggregationBuilder aggregationBuilder = new MinAggregationBuilder("_name").field("number2"); + MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG); + fieldType.setName("number2"); + try (MinAggregator aggregator = createAggregator(aggregationBuilder, fieldType, indexSearcher)) { + aggregator.preCollection(); + indexSearcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + InternalMin result = (InternalMin) aggregator.buildAggregation(0L); + assertEquals(Double.POSITIVE_INFINITY, result.getValue(), 0); + } + indexReader.close(); + directory.close(); + } + + public void testMinAggregator_noDocs() throws Exception { + Directory directory = newDirectory(); + RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); + indexWriter.close(); + + IndexReader indexReader = DirectoryReader.open(directory); + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + + MinAggregationBuilder aggregationBuilder = new MinAggregationBuilder("_name").field("number"); + MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG); + fieldType.setName("number"); + try (MinAggregator aggregator = createAggregator(aggregationBuilder, fieldType, indexSearcher)) { + aggregator.preCollection(); + indexSearcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + InternalMin result = (InternalMin) aggregator.buildAggregation(0L); + assertEquals(Double.POSITIVE_INFINITY, result.getValue(), 0); + } + indexReader.close(); + directory.close(); + } + +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/tophits/TopHitsAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/tophits/TopHitsAggregatorTests.java new file mode 100644 index 00000000000..9bf371e5099 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/tophits/TopHitsAggregatorTests.java @@ -0,0 +1,87 @@ +/* + * 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.metrics.tophits; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.SortedSetDocValuesField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.mapper.KeywordFieldMapper; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.Uid; +import org.elasticsearch.index.mapper.UidFieldMapper; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.aggregations.AggregatorTestCase; +import org.elasticsearch.search.sort.SortOrder; + +public class TopHitsAggregatorTests extends AggregatorTestCase { + + public void testTermsAggregator() throws Exception { + Directory directory = newDirectory(); + RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); + Document document = new Document(); + document.add(new Field(UidFieldMapper.NAME, Uid.createUid("type", "1"), UidFieldMapper.Defaults.FIELD_TYPE)); + document.add(new SortedSetDocValuesField("string", new BytesRef("a"))); + document.add(new SortedSetDocValuesField("string", new BytesRef("b"))); + indexWriter.addDocument(document); + document = new Document(); + document.add(new Field(UidFieldMapper.NAME, Uid.createUid("type", "2"), UidFieldMapper.Defaults.FIELD_TYPE)); + document.add(new SortedSetDocValuesField("string", new BytesRef("c"))); + document.add(new SortedSetDocValuesField("string", new BytesRef("a"))); + indexWriter.addDocument(document); + document = new Document(); + document.add(new Field(UidFieldMapper.NAME, Uid.createUid("type", "3"), UidFieldMapper.Defaults.FIELD_TYPE)); + document.add(new SortedSetDocValuesField("string", new BytesRef("b"))); + document.add(new SortedSetDocValuesField("string", new BytesRef("d"))); + indexWriter.addDocument(document); + indexWriter.close(); + + IndexReader indexReader = DirectoryReader.open(directory); + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + + MappedFieldType fieldType = new KeywordFieldMapper.KeywordFieldType(); + fieldType.setName("string"); + fieldType.setHasDocValues(true ); + TopHitsAggregationBuilder aggregationBuilder = new TopHitsAggregationBuilder("_name"); + aggregationBuilder.sort("string", SortOrder.DESC); + try (TopHitsAggregator aggregator = createAggregator(aggregationBuilder, fieldType, indexSearcher)){ + aggregator.preCollection(); + indexSearcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + TopHits topHits = (TopHits) aggregator.buildAggregation(0L); + SearchHits searchHits = topHits.getHits(); + assertEquals(3L, searchHits.getTotalHits()); + assertEquals("3", searchHits.getAt(0).getId()); + assertEquals("type", searchHits.getAt(0).getType()); + assertEquals("2", searchHits.getAt(1).getId()); + assertEquals("type", searchHits.getAt(1).getType()); + assertEquals("1", searchHits.getAt(2).getId()); + assertEquals("type", searchHits.getAt(2).getType()); + } + indexReader.close(); + directory.close(); + } + +}