From c120f8af89c0a9085aa53ce89a71edde34ed203e Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 29 May 2017 12:47:34 +0200 Subject: [PATCH] Add search method to high level REST client (#24796) Relates to #23331 --- client/rest-high-level/build.gradle | 4 +- .../org/elasticsearch/client/Request.java | 70 +++- .../client/RestHighLevelClient.java | 180 +++++++- .../elasticsearch/client/RequestTests.java | 144 ++++++- .../client/RestHighLevelClientTests.java | 19 +- .../org/elasticsearch/client/SearchIT.java | 395 ++++++++++++++++++ 6 files changed, 784 insertions(+), 28 deletions(-) create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/SearchIT.java diff --git a/client/rest-high-level/build.gradle b/client/rest-high-level/build.gradle index 7c5df5760a2..9203b8978fd 100644 --- a/client/rest-high-level/build.gradle +++ b/client/rest-high-level/build.gradle @@ -26,11 +26,11 @@ group = 'org.elasticsearch.client' dependencies { compile "org.elasticsearch:elasticsearch:${version}" compile "org.elasticsearch.client:rest:${version}" + compile "org.elasticsearch.plugin:parent-join-client:${version}" + compile "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}" testCompile "org.elasticsearch.client:test:${version}" testCompile "org.elasticsearch.test:framework:${version}" - // for parent/child testing - testCompile "org.elasticsearch.plugin:parent-join-client:${version}" testCompile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" testCompile "junit:junit:${versions.junit}" testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}" diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java index d1f3f06eb21..d62f47c0e31 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java @@ -33,7 +33,9 @@ import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.ActiveShardCount; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.common.Nullable; @@ -42,11 +44,13 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; +import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import java.io.ByteArrayOutputStream; @@ -59,7 +63,7 @@ import java.util.StringJoiner; final class Request { - private static final String DELIMITER = "/"; + private static final XContentType REQUEST_BODY_CONTENT_TYPE = XContentType.JSON; final String method; final String endpoint; @@ -79,6 +83,7 @@ final class Request { "method='" + method + '\'' + ", endpoint='" + endpoint + '\'' + ", params=" + params + + ", hasBody=" + (entity != null) + '}'; } @@ -307,23 +312,48 @@ final class Request { xContentType = Requests.INDEX_CONTENT_TYPE; } - BytesRef source = XContentHelper.toXContent(updateRequest, xContentType, false).toBytesRef(); - HttpEntity entity = new ByteArrayEntity(source.bytes, source.offset, source.length, ContentType.create(xContentType.mediaType())); - + HttpEntity entity = createEntity(updateRequest, xContentType); return new Request(HttpPost.METHOD_NAME, endpoint, parameters.getParams(), entity); } + static Request search(SearchRequest searchRequest) throws IOException { + String endpoint = endpoint(searchRequest.indices(), searchRequest.types(), "_search"); + Params params = Params.builder(); + params.putParam(RestSearchAction.TYPED_KEYS_PARAM, "true"); + params.withRouting(searchRequest.routing()); + params.withPreference(searchRequest.preference()); + params.withIndicesOptions(searchRequest.indicesOptions()); + params.putParam("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT)); + if (searchRequest.requestCache() != null) { + params.putParam("request_cache", Boolean.toString(searchRequest.requestCache())); + } + params.putParam("batched_reduce_size", Integer.toString(searchRequest.getBatchedReduceSize())); + if (searchRequest.scroll() != null) { + params.putParam("scroll", searchRequest.scroll().keepAlive()); + } + HttpEntity entity = null; + if (searchRequest.source() != null) { + entity = createEntity(searchRequest.source(), REQUEST_BODY_CONTENT_TYPE); + } + return new Request(HttpGet.METHOD_NAME, endpoint, params.getParams(), entity); + } + + private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException { + BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef(); + return new ByteArrayEntity(source.bytes, source.offset, source.length, ContentType.create(xContentType.mediaType())); + } + + static String endpoint(String[] indices, String[] types, String endpoint) { + return endpoint(String.join(",", indices), String.join(",", types), endpoint); + } + /** * Utility method to build request's endpoint. */ static String endpoint(String... parts) { - if (parts == null || parts.length == 0) { - return DELIMITER; - } - - StringJoiner joiner = new StringJoiner(DELIMITER, DELIMITER, ""); + StringJoiner joiner = new StringJoiner("/", "/", ""); for (String part : parts) { - if (part != null) { + if (Strings.hasLength(part)) { joiner.add(part); } } @@ -453,6 +483,26 @@ final class Request { return this; } + Params withIndicesOptions (IndicesOptions indicesOptions) { + putParam("ignore_unavailable", Boolean.toString(indicesOptions.ignoreUnavailable())); + putParam("allow_no_indices", Boolean.toString(indicesOptions.allowNoIndices())); + String expandWildcards; + if (indicesOptions.expandWildcardsOpen() == false && indicesOptions.expandWildcardsClosed() == false) { + expandWildcards = "none"; + } else { + StringJoiner joiner = new StringJoiner(","); + if (indicesOptions.expandWildcardsOpen()) { + joiner.add("open"); + } + if (indicesOptions.expandWildcardsClosed()) { + joiner.add("closed"); + } + expandWildcards = joiner.toString(); + } + putParam("expand_wildcards", expandWildcards); + return this; + } + Map getParams() { return Collections.unmodifiableMap(params); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java index 4e30311c475..47645817c84 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java @@ -36,22 +36,117 @@ import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.main.MainRequest; import org.elasticsearch.action.main.MainResponse; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.common.CheckedFunction; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ContextParser; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.join.aggregations.ChildrenAggregationBuilder; +import org.elasticsearch.join.aggregations.ParsedChildren; import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.adjacency.ParsedAdjacencyMatrix; +import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; +import org.elasticsearch.search.aggregations.bucket.filters.FiltersAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.filters.ParsedFilters; +import org.elasticsearch.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.geogrid.ParsedGeoHashGrid; +import org.elasticsearch.search.aggregations.bucket.global.GlobalAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.global.ParsedGlobal; +import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; +import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram; +import org.elasticsearch.search.aggregations.bucket.missing.MissingAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.missing.ParsedMissing; +import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested; +import org.elasticsearch.search.aggregations.bucket.nested.ParsedReverseNested; +import org.elasticsearch.search.aggregations.bucket.nested.ReverseNestedAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.range.ParsedRange; +import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.range.date.DateRangeAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.range.date.ParsedDateRange; +import org.elasticsearch.search.aggregations.bucket.range.geodistance.GeoDistanceAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.range.geodistance.ParsedGeoDistance; +import org.elasticsearch.search.aggregations.bucket.sampler.InternalSampler; +import org.elasticsearch.search.aggregations.bucket.sampler.ParsedSampler; +import org.elasticsearch.search.aggregations.bucket.significant.ParsedSignificantLongTerms; +import org.elasticsearch.search.aggregations.bucket.significant.ParsedSignificantStringTerms; +import org.elasticsearch.search.aggregations.bucket.significant.SignificantLongTerms; +import org.elasticsearch.search.aggregations.bucket.significant.SignificantStringTerms; +import org.elasticsearch.search.aggregations.bucket.terms.DoubleTerms; +import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedDoubleTerms; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms; +import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; +import org.elasticsearch.search.aggregations.matrix.stats.MatrixStatsAggregationBuilder; +import org.elasticsearch.search.aggregations.matrix.stats.ParsedMatrixStats; +import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg; +import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.cardinality.ParsedCardinality; +import org.elasticsearch.search.aggregations.metrics.geobounds.GeoBoundsAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.geobounds.ParsedGeoBounds; +import org.elasticsearch.search.aggregations.metrics.geocentroid.GeoCentroidAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.geocentroid.ParsedGeoCentroid; +import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.max.ParsedMax; +import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.min.ParsedMin; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.ParsedHDRPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.ParsedHDRPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentiles; +import org.elasticsearch.search.aggregations.metrics.scripted.ParsedScriptedMetric; +import org.elasticsearch.search.aggregations.metrics.scripted.ScriptedMetricAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; +import org.elasticsearch.search.aggregations.metrics.stats.StatsAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStatsAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.stats.extended.ParsedExtendedStats; +import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum; +import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount; +import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCountAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.InternalSimpleValue; +import org.elasticsearch.search.aggregations.pipeline.ParsedSimpleValue; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValue; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.ParsedBucketMetricValue; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.ParsedPercentilesBucket; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.PercentilesBucketPipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.ParsedStatsBucket; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.StatsBucketPipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ExtendedStatsBucketPipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ParsedExtendedStatsBucket; +import org.elasticsearch.search.aggregations.pipeline.derivative.DerivativePipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.derivative.ParsedDerivative; +import org.elasticsearch.search.suggest.Suggest; +import org.elasticsearch.search.suggest.completion.CompletionSuggestion; +import org.elasticsearch.search.suggest.phrase.PhraseSuggestion; +import org.elasticsearch.search.suggest.term.TermSuggestion; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Collections.emptySet; @@ -82,10 +177,8 @@ public class RestHighLevelClient { */ protected RestHighLevelClient(RestClient restClient, List namedXContentEntries) { this.client = Objects.requireNonNull(restClient); - this.registry = new NamedXContentRegistry(Stream.of( - getNamedXContents().stream(), - namedXContentEntries.stream() - ).flatMap(Function.identity()).collect(toList())); + this.registry = new NamedXContentRegistry(Stream.of(getDefaultNamedXContents().stream(), namedXContentEntries.stream()) + .flatMap(Function.identity()).collect(toList())); } /** @@ -214,6 +307,24 @@ public class RestHighLevelClient { Collections.singleton(404), headers); } + /** + * Executes a search using the Search api + * + * See Search API on elastic.co + */ + public SearchResponse search(SearchRequest searchRequest, Header... headers) throws IOException { + return performRequestAndParseEntity(searchRequest, Request::search, SearchResponse::fromXContent, emptySet(), headers); + } + + /** + * Asynchronously executes a search using the Search api + * + * See Search API on elastic.co + */ + public void searchAsync(SearchRequest searchRequest, ActionListener listener, Header... headers) { + performRequestAsyncAndParseEntity(searchRequest, Request::search, SearchResponse::fromXContent, listener, emptySet(), headers); + } + private Resp performRequestAndParseEntity(Req request, CheckedFunction requestConverter, CheckedFunction entityParser, @@ -365,9 +476,60 @@ public class RestHighLevelClient { return response.getStatusLine().getStatusCode() == 200; } - static List getNamedXContents() { - List namedXContents = new ArrayList<>(); - //namedXContents.add(new NamedXContentRegistry.Entry(Aggregation.class, new ParseField("sterms"), StringTerms::fromXContent)); - return namedXContents; + static List getDefaultNamedXContents() { + Map> map = new HashMap<>(); + map.put(CardinalityAggregationBuilder.NAME, (p, c) -> ParsedCardinality.fromXContent(p, (String) c)); + map.put(InternalHDRPercentiles.NAME, (p, c) -> ParsedHDRPercentiles.fromXContent(p, (String) c)); + map.put(InternalHDRPercentileRanks.NAME, (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c)); + map.put(InternalTDigestPercentiles.NAME, (p, c) -> ParsedTDigestPercentiles.fromXContent(p, (String) c)); + map.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c)); + map.put(PercentilesBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedPercentilesBucket.fromXContent(p, (String) c)); + map.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c)); + map.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c)); + map.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c)); + map.put(AvgAggregationBuilder.NAME, (p, c) -> ParsedAvg.fromXContent(p, (String) c)); + map.put(ValueCountAggregationBuilder.NAME, (p, c) -> ParsedValueCount.fromXContent(p, (String) c)); + map.put(InternalSimpleValue.NAME, (p, c) -> ParsedSimpleValue.fromXContent(p, (String) c)); + map.put(DerivativePipelineAggregationBuilder.NAME, (p, c) -> ParsedDerivative.fromXContent(p, (String) c)); + map.put(InternalBucketMetricValue.NAME, (p, c) -> ParsedBucketMetricValue.fromXContent(p, (String) c)); + map.put(StatsAggregationBuilder.NAME, (p, c) -> ParsedStats.fromXContent(p, (String) c)); + map.put(StatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedStatsBucket.fromXContent(p, (String) c)); + map.put(ExtendedStatsAggregationBuilder.NAME, (p, c) -> ParsedExtendedStats.fromXContent(p, (String) c)); + map.put(ExtendedStatsBucketPipelineAggregationBuilder.NAME, + (p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c)); + map.put(GeoBoundsAggregationBuilder.NAME, (p, c) -> ParsedGeoBounds.fromXContent(p, (String) c)); + map.put(GeoCentroidAggregationBuilder.NAME, (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c)); + map.put(HistogramAggregationBuilder.NAME, (p, c) -> ParsedHistogram.fromXContent(p, (String) c)); + map.put(DateHistogramAggregationBuilder.NAME, (p, c) -> ParsedDateHistogram.fromXContent(p, (String) c)); + map.put(StringTerms.NAME, (p, c) -> ParsedStringTerms.fromXContent(p, (String) c)); + map.put(LongTerms.NAME, (p, c) -> ParsedLongTerms.fromXContent(p, (String) c)); + map.put(DoubleTerms.NAME, (p, c) -> ParsedDoubleTerms.fromXContent(p, (String) c)); + map.put(MissingAggregationBuilder.NAME, (p, c) -> ParsedMissing.fromXContent(p, (String) c)); + map.put(NestedAggregationBuilder.NAME, (p, c) -> ParsedNested.fromXContent(p, (String) c)); + map.put(ReverseNestedAggregationBuilder.NAME, (p, c) -> ParsedReverseNested.fromXContent(p, (String) c)); + map.put(GlobalAggregationBuilder.NAME, (p, c) -> ParsedGlobal.fromXContent(p, (String) c)); + map.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c)); + map.put(InternalSampler.PARSER_NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); + map.put(GeoGridAggregationBuilder.NAME, (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c)); + map.put(RangeAggregationBuilder.NAME, (p, c) -> ParsedRange.fromXContent(p, (String) c)); + map.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c)); + map.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c)); + map.put(FiltersAggregationBuilder.NAME, (p, c) -> ParsedFilters.fromXContent(p, (String) c)); + map.put(AdjacencyMatrixAggregationBuilder.NAME, (p, c) -> ParsedAdjacencyMatrix.fromXContent(p, (String) c)); + map.put(SignificantLongTerms.NAME, (p, c) -> ParsedSignificantLongTerms.fromXContent(p, (String) c)); + map.put(SignificantStringTerms.NAME, (p, c) -> ParsedSignificantStringTerms.fromXContent(p, (String) c)); + map.put(ScriptedMetricAggregationBuilder.NAME, (p, c) -> ParsedScriptedMetric.fromXContent(p, (String) c)); + map.put(ChildrenAggregationBuilder.NAME, (p, c) -> ParsedChildren.fromXContent(p, (String) c)); + map.put(MatrixStatsAggregationBuilder.NAME, (p, c) -> ParsedMatrixStats.fromXContent(p, (String) c)); + List entries = map.entrySet().stream() + .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) + .collect(Collectors.toList()); + entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(TermSuggestion.NAME), + (parser, context) -> TermSuggestion.fromXContent(parser, (String)context))); + entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(PhraseSuggestion.NAME), + (parser, context) -> PhraseSuggestion.fromXContent(parser, (String)context))); + entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(CompletionSuggestion.NAME), + (parser, context) -> CompletionSuggestion.fromXContent(parser, (String)context))); + return entries; } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java index c527125e10b..12f0d991e7f 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java @@ -21,25 +21,41 @@ package org.elasticsearch.client; import org.apache.http.HttpEntity; import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.util.EntityUtils; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkShardRequest; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.replication.ReplicatedWriteRequest; import org.elasticsearch.action.support.replication.ReplicationRequest; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.query.TermQueryBuilder; +import org.elasticsearch.rest.action.search.RestSearchAction; +import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; +import org.elasticsearch.search.aggregations.support.ValueType; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.collapse.CollapseBuilder; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; +import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; +import org.elasticsearch.search.rescore.QueryRescorerBuilder; +import org.elasticsearch.search.suggest.SuggestBuilder; +import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.RandomObjects; @@ -48,6 +64,7 @@ import java.io.InputStream; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.StringJoiner; import java.util.function.Consumer; import java.util.function.Function; @@ -257,9 +274,8 @@ public class RequestTests extends ESTestCase { assertEquals(method, request.method); HttpEntity entity = request.entity; - assertNotNull(entity); assertTrue(entity instanceof ByteArrayEntity); - + assertEquals(indexRequest.getContentType().mediaType(), entity.getContentType().getValue()); try (XContentParser parser = createParser(xContentType.xContent(), entity.getContent())) { assertEquals(nbFields, parser.map().size()); } @@ -353,7 +369,6 @@ public class RequestTests extends ESTestCase { assertEquals("POST", request.method); HttpEntity entity = request.entity; - assertNotNull(entity); assertTrue(entity instanceof ByteArrayEntity); UpdateRequest parsedUpdateRequest = new UpdateRequest(); @@ -470,7 +485,7 @@ public class RequestTests extends ESTestCase { assertEquals("/_bulk", request.endpoint); assertEquals(expectedParams, request.params); assertEquals("POST", request.method); - + assertEquals(xContentType.mediaType(), request.entity.getContentType().getValue()); byte[] content = new byte[(int) request.entity.getContentLength()]; try (InputStream inputStream = request.entity.getContent()) { Streams.readFully(inputStream, content); @@ -584,6 +599,127 @@ public class RequestTests extends ESTestCase { } } + public void testSearch() throws Exception { + SearchRequest searchRequest = new SearchRequest(); + int numIndices = randomIntBetween(0, 5); + String[] indices = new String[numIndices]; + for (int i = 0; i < numIndices; i++) { + indices[i] = "index-" + randomAlphaOfLengthBetween(2, 5); + } + searchRequest.indices(indices); + int numTypes = randomIntBetween(0, 5); + String[] types = new String[numTypes]; + for (int i = 0; i < numTypes; i++) { + types[i] = "type-" + randomAlphaOfLengthBetween(2, 5); + } + searchRequest.types(types); + + Map expectedParams = new HashMap<>(); + expectedParams.put(RestSearchAction.TYPED_KEYS_PARAM, "true"); + if (randomBoolean()) { + searchRequest.routing(randomAlphaOfLengthBetween(3, 10)); + expectedParams.put("routing", searchRequest.routing()); + } + if (randomBoolean()) { + searchRequest.preference(randomAlphaOfLengthBetween(3, 10)); + expectedParams.put("preference", searchRequest.preference()); + } + if (randomBoolean()) { + searchRequest.searchType(randomFrom(SearchType.values())); + } + expectedParams.put("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT)); + if (randomBoolean()) { + searchRequest.requestCache(randomBoolean()); + expectedParams.put("request_cache", Boolean.toString(searchRequest.requestCache())); + } + if (randomBoolean()) { + searchRequest.setBatchedReduceSize(randomIntBetween(2, Integer.MAX_VALUE)); + } + expectedParams.put("batched_reduce_size", Integer.toString(searchRequest.getBatchedReduceSize())); + if (randomBoolean()) { + searchRequest.scroll(randomTimeValue()); + expectedParams.put("scroll", searchRequest.scroll().keepAlive().getStringRep()); + } + + if (randomBoolean()) { + searchRequest.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean())); + } + expectedParams.put("ignore_unavailable", Boolean.toString(searchRequest.indicesOptions().ignoreUnavailable())); + expectedParams.put("allow_no_indices", Boolean.toString(searchRequest.indicesOptions().allowNoIndices())); + if (searchRequest.indicesOptions().expandWildcardsOpen() && searchRequest.indicesOptions().expandWildcardsClosed()) { + expectedParams.put("expand_wildcards", "open,closed"); + } else if (searchRequest.indicesOptions().expandWildcardsOpen()) { + expectedParams.put("expand_wildcards", "open"); + } else if (searchRequest.indicesOptions().expandWildcardsClosed()) { + expectedParams.put("expand_wildcards", "closed"); + } else { + expectedParams.put("expand_wildcards", "none"); + } + + SearchSourceBuilder searchSourceBuilder = null; + if (frequently()) { + searchSourceBuilder = new SearchSourceBuilder(); + if (randomBoolean()) { + searchSourceBuilder.size(randomIntBetween(0, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + searchSourceBuilder.from(randomIntBetween(0, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + searchSourceBuilder.minScore(randomFloat()); + } + if (randomBoolean()) { + searchSourceBuilder.explain(randomBoolean()); + } + if (randomBoolean()) { + searchSourceBuilder.profile(randomBoolean()); + } + if (randomBoolean()) { + searchSourceBuilder.highlighter(new HighlightBuilder().field(randomAlphaOfLengthBetween(3, 10))); + } + if (randomBoolean()) { + searchSourceBuilder.query(new TermQueryBuilder(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10))); + } + if (randomBoolean()) { + searchSourceBuilder.aggregation(new TermsAggregationBuilder(randomAlphaOfLengthBetween(3, 10), ValueType.STRING) + .field(randomAlphaOfLengthBetween(3, 10))); + } + if (randomBoolean()) { + searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion(randomAlphaOfLengthBetween(3, 10), + new CompletionSuggestionBuilder(randomAlphaOfLengthBetween(3, 10)))); + } + if (randomBoolean()) { + searchSourceBuilder.addRescorer(new QueryRescorerBuilder( + new TermQueryBuilder(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10)))); + } + if (randomBoolean()) { + searchSourceBuilder.collapse(new CollapseBuilder(randomAlphaOfLengthBetween(3, 10))); + } + searchRequest.source(searchSourceBuilder); + } + + Request request = Request.search(searchRequest); + StringJoiner endpoint = new StringJoiner("/", "/", ""); + String index = String.join(",", indices); + if (Strings.hasLength(index)) { + endpoint.add(index); + } + String type = String.join(",", types); + if (Strings.hasLength(type)) { + endpoint.add(type); + } + endpoint.add("_search"); + assertEquals(endpoint.toString(), request.endpoint); + assertEquals(expectedParams, request.params); + if (searchSourceBuilder == null) { + assertNull(request.entity); + } else { + BytesReference expectedBytes = XContentHelper.toXContent(searchSourceBuilder, XContentType.JSON, false); + assertEquals(XContentType.JSON.mediaType(), request.entity.getContentType().getValue()); + assertEquals(expectedBytes, new BytesArray(EntityUtils.toByteArray(request.entity))); + } + } + public void testParams() { final int nbParams = randomIntBetween(0, 10); Request.Params params = Request.Params.builder(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java index 7104ee9e39d..05883a066a5 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.client; import com.fasterxml.jackson.core.JsonParseException; - import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -51,6 +50,8 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.cbor.CborXContent; import org.elasticsearch.common.xcontent.smile.SmileXContent; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.suggest.Suggest; import org.elasticsearch.test.ESTestCase; import org.junit.Before; import org.mockito.ArgumentMatcher; @@ -61,7 +62,9 @@ import org.mockito.internal.matchers.VarargMatcher; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -566,8 +569,18 @@ public class RestHighLevelClientTests extends ESTestCase { } public void testNamedXContents() { - List namedXContents = RestHighLevelClient.getNamedXContents(); - assertEquals(0, namedXContents.size()); + List namedXContents = RestHighLevelClient.getDefaultNamedXContents(); + assertEquals(45, namedXContents.size()); + Map, Integer> categories = new HashMap<>(); + for (NamedXContentRegistry.Entry namedXContent : namedXContents) { + Integer counter = categories.putIfAbsent(namedXContent.categoryClass, 1); + if (counter != null) { + categories.put(namedXContent.categoryClass, counter + 1); + } + } + assertEquals(2, categories.size()); + assertEquals(Integer.valueOf(42), categories.get(Aggregation.class)); + assertEquals(Integer.valueOf(3), categories.get(Suggest.Suggestion.class)); } private static class TrackingActionListener implements ActionListener { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SearchIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SearchIT.java new file mode 100644 index 00000000000..7aeee5bf47f --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SearchIT.java @@ -0,0 +1,395 @@ +/* + * 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.client; + +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.index.query.MatchQueryBuilder; +import org.elasticsearch.join.aggregations.Children; +import org.elasticsearch.join.aggregations.ChildrenAggregationBuilder; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.aggregations.bucket.range.Range; +import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.terms.Terms; +import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; +import org.elasticsearch.search.aggregations.matrix.stats.MatrixStats; +import org.elasticsearch.search.aggregations.matrix.stats.MatrixStatsAggregationBuilder; +import org.elasticsearch.search.aggregations.support.ValueType; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.suggest.Suggest; +import org.elasticsearch.search.suggest.SuggestBuilder; +import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder; +import org.junit.Before; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; + +import static org.hamcrest.Matchers.both; +import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; + +public class SearchIT extends ESRestHighLevelClientTestCase { + + @Before + public void indexDocuments() throws IOException { + StringEntity doc1 = new StringEntity("{\"type\":\"type1\", \"num\":10, \"num2\":50}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "/index/type/1", Collections.emptyMap(), doc1); + StringEntity doc2 = new StringEntity("{\"type\":\"type1\", \"num\":20, \"num2\":40}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "/index/type/2", Collections.emptyMap(), doc2); + StringEntity doc3 = new StringEntity("{\"type\":\"type1\", \"num\":50, \"num2\":35}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "/index/type/3", Collections.emptyMap(), doc3); + StringEntity doc4 = new StringEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "/index/type/4", Collections.emptyMap(), doc4); + StringEntity doc5 = new StringEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "/index/type/5", Collections.emptyMap(), doc5); + client().performRequest("POST", "/index/_refresh"); + } + + public void testSearchNoQuery() throws IOException { + SearchRequest searchRequest = new SearchRequest(); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getAggregations()); + assertNull(searchResponse.getSuggest()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertEquals(5, searchResponse.getHits().totalHits); + assertEquals(5, searchResponse.getHits().getHits().length); + for (SearchHit searchHit : searchResponse.getHits().getHits()) { + assertEquals("index", searchHit.getIndex()); + assertEquals("type", searchHit.getType()); + assertThat(Integer.valueOf(searchHit.getId()), both(greaterThan(0)).and(lessThan(6))); + assertEquals(1.0f, searchHit.getScore(), 0); + assertEquals(-1L, searchHit.getVersion()); + assertNotNull(searchHit.getSourceAsMap()); + assertEquals(3, searchHit.getSourceAsMap().size()); + assertTrue(searchHit.getSourceAsMap().containsKey("type")); + assertTrue(searchHit.getSourceAsMap().containsKey("num")); + assertTrue(searchHit.getSourceAsMap().containsKey("num2")); + } + } + + public void testSearchMatchQuery() throws IOException { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(new SearchSourceBuilder().query(new MatchQueryBuilder("num", 10))); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getAggregations()); + assertNull(searchResponse.getSuggest()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertEquals(1, searchResponse.getHits().totalHits); + assertEquals(1, searchResponse.getHits().getHits().length); + assertThat(searchResponse.getHits().getMaxScore(), greaterThan(0f)); + SearchHit searchHit = searchResponse.getHits().getHits()[0]; + assertEquals("index", searchHit.getIndex()); + assertEquals("type", searchHit.getType()); + assertEquals("1", searchHit.getId()); + assertThat(searchHit.getScore(), greaterThan(0f)); + assertEquals(-1L, searchHit.getVersion()); + assertNotNull(searchHit.getSourceAsMap()); + assertEquals(3, searchHit.getSourceAsMap().size()); + assertEquals("type1", searchHit.getSourceAsMap().get("type")); + assertEquals(50, searchHit.getSourceAsMap().get("num2")); + } + + public void testSearchWithTermsAgg() throws IOException { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.aggregation(new TermsAggregationBuilder("agg1", ValueType.STRING).field("type.keyword")); + searchSourceBuilder.size(0); + searchRequest.source(searchSourceBuilder); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getSuggest()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertEquals(0, searchResponse.getHits().getHits().length); + assertEquals(0f, searchResponse.getHits().getMaxScore(), 0f); + Terms termsAgg = searchResponse.getAggregations().get("agg1"); + assertEquals("agg1", termsAgg.getName()); + assertEquals(2, termsAgg.getBuckets().size()); + Terms.Bucket type1 = termsAgg.getBucketByKey("type1"); + assertEquals(3, type1.getDocCount()); + assertEquals(0, type1.getAggregations().asList().size()); + Terms.Bucket type2 = termsAgg.getBucketByKey("type2"); + assertEquals(2, type2.getDocCount()); + assertEquals(0, type2.getAggregations().asList().size()); + } + + public void testSearchWithRangeAgg() throws IOException { + { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.aggregation(new RangeAggregationBuilder("agg1").field("num")); + searchSourceBuilder.size(0); + searchRequest.source(searchSourceBuilder); + + ElasticsearchStatusException exception = expectThrows(ElasticsearchStatusException.class, + () -> execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync)); + assertEquals(RestStatus.BAD_REQUEST, exception.status()); + } + + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.aggregation(new RangeAggregationBuilder("agg1").field("num") + .addRange("first", 0, 30).addRange("second", 31, 200)); + searchSourceBuilder.size(0); + searchRequest.source(searchSourceBuilder); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getSuggest()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertThat(searchResponse.getTook().nanos(), greaterThan(0L)); + assertEquals(5, searchResponse.getHits().totalHits); + assertEquals(0, searchResponse.getHits().getHits().length); + assertEquals(0f, searchResponse.getHits().getMaxScore(), 0f); + Range rangeAgg = searchResponse.getAggregations().get("agg1"); + assertEquals("agg1", rangeAgg.getName()); + assertEquals(2, rangeAgg.getBuckets().size()); + { + Range.Bucket bucket = rangeAgg.getBuckets().get(0); + assertEquals("first", bucket.getKeyAsString()); + assertEquals(2, bucket.getDocCount()); + } + { + Range.Bucket bucket = rangeAgg.getBuckets().get(1); + assertEquals("second", bucket.getKeyAsString()); + assertEquals(3, bucket.getDocCount()); + } + } + + public void testSearchWithTermsAndRangeAgg() throws IOException { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + TermsAggregationBuilder agg = new TermsAggregationBuilder("agg1", ValueType.STRING).field("type.keyword"); + agg.subAggregation(new RangeAggregationBuilder("subagg").field("num") + .addRange("first", 0, 30).addRange("second", 31, 200)); + searchSourceBuilder.aggregation(agg); + searchSourceBuilder.size(0); + searchRequest.source(searchSourceBuilder); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getSuggest()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertEquals(0, searchResponse.getHits().getHits().length); + assertEquals(0f, searchResponse.getHits().getMaxScore(), 0f); + Terms termsAgg = searchResponse.getAggregations().get("agg1"); + assertEquals("agg1", termsAgg.getName()); + assertEquals(2, termsAgg.getBuckets().size()); + Terms.Bucket type1 = termsAgg.getBucketByKey("type1"); + assertEquals(3, type1.getDocCount()); + assertEquals(1, type1.getAggregations().asList().size()); + { + Range rangeAgg = type1.getAggregations().get("subagg"); + assertEquals(2, rangeAgg.getBuckets().size()); + { + Range.Bucket bucket = rangeAgg.getBuckets().get(0); + assertEquals("first", bucket.getKeyAsString()); + assertEquals(2, bucket.getDocCount()); + } + { + Range.Bucket bucket = rangeAgg.getBuckets().get(1); + assertEquals("second", bucket.getKeyAsString()); + assertEquals(1, bucket.getDocCount()); + } + } + Terms.Bucket type2 = termsAgg.getBucketByKey("type2"); + assertEquals(2, type2.getDocCount()); + assertEquals(1, type2.getAggregations().asList().size()); + { + Range rangeAgg = type2.getAggregations().get("subagg"); + assertEquals(2, rangeAgg.getBuckets().size()); + { + Range.Bucket bucket = rangeAgg.getBuckets().get(0); + assertEquals("first", bucket.getKeyAsString()); + assertEquals(0, bucket.getDocCount()); + } + { + Range.Bucket bucket = rangeAgg.getBuckets().get(1); + assertEquals("second", bucket.getKeyAsString()); + assertEquals(2, bucket.getDocCount()); + } + } + } + + public void testSearchWithMatrixStats() throws IOException { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.aggregation(new MatrixStatsAggregationBuilder("agg1").fields(Arrays.asList("num", "num2"))); + searchSourceBuilder.size(0); + searchRequest.source(searchSourceBuilder); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getSuggest()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertThat(searchResponse.getTook().nanos(), greaterThan(0L)); + assertEquals(5, searchResponse.getHits().totalHits); + assertEquals(0, searchResponse.getHits().getHits().length); + assertEquals(0f, searchResponse.getHits().getMaxScore(), 0f); + assertEquals(1, searchResponse.getAggregations().asList().size()); + MatrixStats matrixStats = searchResponse.getAggregations().get("agg1"); + assertEquals(5, matrixStats.getFieldCount("num")); + assertEquals(56d, matrixStats.getMean("num"), 0d); + assertEquals(1830d, matrixStats.getVariance("num"), 0d); + assertEquals(0.09340198804973057, matrixStats.getSkewness("num"), 0d); + assertEquals(1.2741646510794589, matrixStats.getKurtosis("num"), 0d); + assertEquals(5, matrixStats.getFieldCount("num2")); + assertEquals(29d, matrixStats.getMean("num2"), 0d); + assertEquals(330d, matrixStats.getVariance("num2"), 0d); + assertEquals(-0.13568039346585542, matrixStats.getSkewness("num2"), 0d); + assertEquals(1.3517561983471074, matrixStats.getKurtosis("num2"), 0d); + assertEquals(-767.5, matrixStats.getCovariance("num", "num2"), 0d); + assertEquals(-0.9876336291667923, matrixStats.getCorrelation("num", "num2"), 0d); + } + + public void testSearchWithParentJoin() throws IOException { + StringEntity parentMapping = new StringEntity("{\n" + + " \"mappings\": {\n" + + " \"answer\" : {\n" + + " \"_parent\" : {\n" + + " \"type\" : \"question\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"settings\": {\n" + + " \"index.mapping.single_type\": false" + + " }\n" + + "}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "/child_example", Collections.emptyMap(), parentMapping); + StringEntity questionDoc = new StringEntity("{\n" + + " \"body\": \"

I have Windows 2003 server and i bought a new Windows 2008 server...\",\n" + + " \"title\": \"Whats the best way to file transfer my site from server to a newer one?\",\n" + + " \"tags\": [\n" + + " \"windows-server-2003\",\n" + + " \"windows-server-2008\",\n" + + " \"file-transfer\"\n" + + " ]\n" + + "}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "/child_example/question/1", Collections.emptyMap(), questionDoc); + StringEntity answerDoc1 = new StringEntity("{\n" + + " \"owner\": {\n" + + " \"location\": \"Norfolk, United Kingdom\",\n" + + " \"display_name\": \"Sam\",\n" + + " \"id\": 48\n" + + " },\n" + + " \"body\": \"

Unfortunately you're pretty much limited to FTP...\",\n" + + " \"creation_date\": \"2009-05-04T13:45:37.030\"\n" + + "}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "child_example/answer/1", Collections.singletonMap("parent", "1"), answerDoc1); + StringEntity answerDoc2 = new StringEntity("{\n" + + " \"owner\": {\n" + + " \"location\": \"Norfolk, United Kingdom\",\n" + + " \"display_name\": \"Troll\",\n" + + " \"id\": 49\n" + + " },\n" + + " \"body\": \"

Use Linux...\",\n" + + " \"creation_date\": \"2009-05-05T13:45:37.030\"\n" + + "}", ContentType.APPLICATION_JSON); + client().performRequest("PUT", "/child_example/answer/2", Collections.singletonMap("parent", "1"), answerDoc2); + client().performRequest("POST", "/_refresh"); + + TermsAggregationBuilder leafTermAgg = new TermsAggregationBuilder("top-names", ValueType.STRING) + .field("owner.display_name.keyword").size(10); + ChildrenAggregationBuilder childrenAgg = new ChildrenAggregationBuilder("to-answers", "answer").subAggregation(leafTermAgg); + TermsAggregationBuilder termsAgg = new TermsAggregationBuilder("top-tags", ValueType.STRING).field("tags.keyword") + .size(10).subAggregation(childrenAgg); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.size(0).aggregation(termsAgg); + SearchRequest searchRequest = new SearchRequest("child_example"); + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getSuggest()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertThat(searchResponse.getTook().nanos(), greaterThan(0L)); + assertEquals(3, searchResponse.getHits().totalHits); + assertEquals(0, searchResponse.getHits().getHits().length); + assertEquals(0f, searchResponse.getHits().getMaxScore(), 0f); + assertEquals(1, searchResponse.getAggregations().asList().size()); + Terms terms = searchResponse.getAggregations().get("top-tags"); + assertEquals(0, terms.getDocCountError()); + assertEquals(0, terms.getSumOfOtherDocCounts()); + assertEquals(3, terms.getBuckets().size()); + for (Terms.Bucket bucket : terms.getBuckets()) { + assertThat(bucket.getKeyAsString(), + either(equalTo("file-transfer")).or(equalTo("windows-server-2003")).or(equalTo("windows-server-2008"))); + assertEquals(1, bucket.getDocCount()); + assertEquals(1, bucket.getAggregations().asList().size()); + Children children = bucket.getAggregations().get("to-answers"); + assertEquals(2, children.getDocCount()); + assertEquals(1, children.getAggregations().asList().size()); + Terms leafTerms = children.getAggregations().get("top-names"); + assertEquals(0, leafTerms.getDocCountError()); + assertEquals(0, leafTerms.getSumOfOtherDocCounts()); + assertEquals(2, leafTerms.getBuckets().size()); + assertEquals(2, leafTerms.getBuckets().size()); + Terms.Bucket sam = leafTerms.getBucketByKey("Sam"); + assertEquals(1, sam.getDocCount()); + Terms.Bucket troll = leafTerms.getBucketByKey("Troll"); + assertEquals(1, troll.getDocCount()); + } + } + + public void testSearchWithSuggest() throws IOException { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion("sugg1", new PhraseSuggestionBuilder("type")) + .setGlobalText("type")); + searchSourceBuilder.size(0); + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getAggregations()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertEquals(0, searchResponse.getHits().totalHits); + assertEquals(0f, searchResponse.getHits().getMaxScore(), 0f); + assertEquals(0, searchResponse.getHits().getHits().length); + assertEquals(1, searchResponse.getSuggest().size()); + + Suggest.Suggestion> sugg = searchResponse + .getSuggest().iterator().next(); + assertEquals("sugg1", sugg.getName()); + for (Suggest.Suggestion.Entry options : sugg) { + assertEquals("type", options.getText().string()); + assertEquals(0, options.getOffset()); + assertEquals(4, options.getLength()); + assertEquals(2 ,options.getOptions().size()); + for (Suggest.Suggestion.Entry.Option option : options) { + assertThat(option.getScore(), greaterThan(0f)); + assertThat(option.getText().string(), either(equalTo("type1")).or(equalTo("type2"))); + } + } + } + + private static void assertSearchHeader(SearchResponse searchResponse) { + assertThat(searchResponse.getTook().nanos(), greaterThan(0L)); + assertEquals(0, searchResponse.getFailedShards()); + assertThat(searchResponse.getTotalShards(), greaterThan(0)); + assertEquals(searchResponse.getTotalShards(), searchResponse.getSuccessfulShards()); + assertEquals(0, searchResponse.getShardFailures().length); + } +}